Définition de méthodes
Modifions la fonction area
qui prend une instance de Rectangle
en paramètre et définissons plutôt une méthode area
sur la structure Rectangle
, comme indiqué dans la Liste 5-13.
Nom de fichier : src/main.rs
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
1 impl Rectangle {
2 fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
3 rect1.area()
);
}
Liste 5-13 : Définition d'une méthode area
sur la structure Rectangle
Pour définir la fonction dans le contexte de Rectangle
, nous commençons un bloc impl
(implémentation) pour Rectangle
[1]. Tout ce qui se trouve à l'intérieur de ce bloc impl
sera associé au type Rectangle
. Ensuite, nous déplaçons la fonction area
à l'intérieur des accolades impl
[2] et changeons le premier (et dans ce cas, unique) paramètre pour qu'il soit self
dans la signature et partout dans le corps. Dans main
, où nous appelions la fonction area
et passions rect1
en argument, nous pouvons maintenant utiliser la syntaxe des méthodes pour appeler la méthode area
sur notre instance de Rectangle
[3]. La syntaxe des méthodes suit une instance : nous ajoutons un point suivi du nom de la méthode, des parenthèses et de tout argument.
Dans la signature de area
, nous utilisons &self
au lieu de rectangle: &Rectangle
. Le &self
est en fait une abréviation de self: &Self
. Dans un bloc impl
, le type Self
est un alias pour le type sur lequel porte le bloc impl
. Les méthodes doivent avoir un paramètre nommé self
du type Self
pour leur premier paramètre, donc Rust vous permet d'abréger cela en utilisant seulement le nom self
dans le premier emplacement de paramètre. Notez que nous devons toujours utiliser le &
devant l'abréviation self
pour indiquer que cette méthode emprunte l'instance Self
, tout comme nous l'avons fait avec rectangle: &Rectangle
. Les méthodes peuvent prendre la propriété de self
, emprunter self
de manière immuable, comme nous l'avons fait ici, ou emprunter self
de manière mutable, tout comme elles peuvent tout autre paramètre.
Nous avons choisi &self
ici pour la même raison que nous avons utilisé &Rectangle
dans la version fonction : nous ne voulons pas prendre la propriété, et nous ne voulons que lire les données dans la structure, pas les modifier. Si nous avions voulu modifier l'instance sur laquelle nous avons appelé la méthode en tant que partie de ce que fait la méthode, nous aurions utilisé &mut self
comme premier paramètre. Il est rare d'avoir une méthode qui prend la propriété de l'instance en utilisant seulement self
comme premier paramètre ; cette technique est généralement utilisée lorsque la méthode transforme self
en quelque chose d'autre et que vous voulez empêcher l'appelant d'utiliser l'instance d'origine après la transformation.
La principale raison d'utiliser des méthodes plutôt que des fonctions, en plus de fournir la syntaxe des méthodes et de ne pas avoir à répéter le type de self
dans la signature de chaque méthode, est pour l'organisation. Nous avons regroupé tout ce que nous pouvons faire avec une instance d'un type dans un seul bloc impl
plutôt que de forcer les futurs utilisateurs de notre code à chercher les capacités de Rectangle
à différents endroits dans la bibliothèque que nous fournissons.
Notez que nous pouvons choisir de donner à une méthode le même nom qu'un des champs de la structure. Par exemple, nous pouvons définir une méthode sur Rectangle
qui s'appelle également width
:
Nom de fichier : src/main.rs
impl Rectangle {
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
if rect1.width() {
println!(
"The rectangle has a nonzero width; it is {}",
rect1.width
);
}
}
Ici, nous choisissons de faire en sorte que la méthode width
renvoie true
si la valeur dans le champ width
de l'instance est supérieure à 0
et false
si la valeur est 0
: nous pouvons utiliser un champ dans une méthode du même nom pour n'importe quel but. Dans main
, lorsque nous suivons rect1.width
de parenthèses, Rust sait que nous voulons dire la méthode width
. Lorsque nous n'utilisons pas de parenthèses, Rust sait que nous voulons dire le champ width
.
Souvent, mais pas toujours, lorsque nous donnons à des méthodes le même nom qu'un champ, nous voulons qu'elles ne fassent que renvoyer la valeur dans le champ et ne fassent rien d'autre. Les méthodes comme celles-ci sont appelées getters, et Rust ne les implémente pas automatiquement pour les champs de structure comme le font certaines autres langues. Les getters sont utiles car vous pouvez rendre le champ privé mais la méthode publique, et ainsi autoriser un accès en lecture seulement à ce champ en tant que partie de l'API publique du type. Nous discuterons de ce qu'est public et privé et de la manière de désigner un champ ou une méthode comme public ou privé au Chapitre 7.
Où est l'opérateur ->?
En C et C++, deux opérateurs différents sont utilisés pour appeler des méthodes : vous utilisez .
si vous appelez une méthode directement sur l'objet et ->
si vous appelez la méthode sur un pointeur vers l'objet et que vous devez d'abord déréférencer le pointeur. En d'autres termes, si object
est un pointeur, object->
quelque chose()
est similaire à (*object).
quelque chose()
.
Rust n'a pas d'équivalent à l'opérateur ->
; au lieu de cela, Rust a une fonctionnalité appelée référence et déréférence automatiques. Appeler des méthodes est l'un des rares endroits dans Rust où ce comportement existe.
Voici comment cela fonctionne : lorsque vous appelez une méthode avec object.
quelque chose()
, Rust ajoute automatiquement &
, &mut
ou *
de sorte que object
corresponde à la signature de la méthode. En d'autres termes, les suivantes sont équivalentes :
p1.distance(&p2);
(&p1).distance(&p2);
La première semble beaucoup plus propre. Ce comportement de référence automatique fonctionne car les méthodes ont un récepteur clair - le type de self
. Étant donné le récepteur et le nom d'une méthode, Rust peut déterminer de manière définitive si la méthode lit (&self
), modifie (&mut self
) ou consomme (self
). Le fait que Rust rende l'emprunt implicite pour les récepteurs de méthodes est une grande partie de ce qui rend la propriété ergonomique en pratique.