Les traits et les durées de vie sont des éléments clés de Rust. Vous pouvez utiliser les traits pour définir des comportements et des capacités que les types doivent implémenter. Ils sont très polyvalents et permettent d’écrire du code plus générique, de réduire la duplication et d’améliorer la maintenabilité.
Rust utilise un autre mécanisme – les durées de vie – pour suivre la propriété des variables dans et hors du champ d’application. Cela permet d’éviter les pointeurs pendants lors de la désallocation des variables.
Ensemble, les traits et les durées de vie permettent de garantir la sécurité des types, la sécurité de la mémoire et la fiabilité du code.
Comprendre les traits en Rust
Les traits sont des collections de méthodes que d’autres types peuvent implémenter. Les traits sont similaires aux interfaces dans des langages comme Java, Go et TypeScript, mais ils sont plus flexibles.
Vous utiliserez la méthode trait mot-clé pour définir les traits en Rust, suivi d’une déclaration des signatures de méthodes.
trait MyTrait {
fn my_method(&self);
}
Le code définit un trait nommé MonTrait avec un ma_méthode méthode. La méthode &self indique que la méthode fait référence à l’objet du type d’implémentation en tant que premier paramètre.
Après avoir défini un trait, vous pouvez l’implémenter pour vos types personnalisés.
Voici comment implémenter un trait pour vos types struct.
struct Person {
name: String,
age: u32,
}
impl Info for Person {
fn summary(&self) {
println!("My name is {} and I am {} years old.", self.name, self.age);
}
}
L’outil La personne struct met en œuvre Infoet vous pouvez appeler le résumé La méthode Personne struct.
fn main(){
let john = Person { name: String::from("John"), age: 30 };
john.summary(); // Output: My name is John, and I am 30 years old.
}
La john La variable est une instance de la classe Personne struct.
La principal appels de fonction résumé qui imprime un message sur la console :
Les enums peuvent implémenter des traits. Voici comment vous pouvez définir un enum avec des variantes qui implémentent le trait résumé méthode :
enum MyEnum {
VariantA,
VariantB,
}
impl Info for MyEnum {
fn summary(&self) {
match self {
MyEnum::VariantA => {
// implementation for VariantA
}
MyEnum::VariantB => {
// implementation for VariantB
}
}
}
}
Utilisation des Traits pour les paramètres de fonction et les valeurs de retour
Vous pouvez utiliser des traits comme paramètres de fonction et valeurs de retour. L’utilisation de traits comme paramètres de fonction est pratique pour écrire du code générique avec plusieurs types.
Voici une fonction qui prend un paramètre de n’importe quel type qui implémente la fonction Info.
fn do_something<T: Info>(value: T) {
value.summary();
}
Le <T : Info> ; La syntaxe spécifie que T doit être mis en œuvre Info. Vous pouvez appeler le résumé fonction avec n’importe quelle valeur qui implémente Info.
La vie en rouille
L’outil de vérification des emprunts de Rust analyse les programmes et s’assure de la bonne utilisation de la mémoire. En Rust, chaque valeur a un propriétaire qui est responsable de la désallocation de la valeur. Lorsque les variables empruntent des valeurs, elles empruntent une référence à la valeur passée, mais le propriétaire en conserve la propriété.
Les durées de vie sont un moyen de s’assurer que les valeurs empruntées sont utilisées correctement. Une durée de vie est une étiquette attachée à une référence, décrivant la durée de validité de la référence.
En Rust, vous pouvez spécifier une durée de vie en utilisant une annotation de type apostrophe :
func<'a>
Lors de la création d’une référence, celle-ci se voit attribuer une durée de vie qui décrit sa durée de validité. Si vous avez une fonction qui prend la référence à une valeur, la durée de vie doit être plus longue que l’appel de la fonction pour s’assurer que la valeur est valide lorsque la fonction revient.
Voici un exemple de spécification de durée de vie dans une fonction.
fn do_something<'a>(x: &'a i32) -> &'a i32 {
x
}
fn main() {
let x = 42;
let result = do_something(&x);
println!("The result is: {}", result);
}
Dans la fonction faire_quelque chose la fonction ‘a Le paramètre de durée de vie indique que la référence à x est valable aussi longtemps que l’appel de la fonction. La référence renvoyée est également valide aussi longtemps que l’appel de la fonction.
La fonction principal imprime le résultat en passant une référence à la fonction x variable dans le principal à la console.
La syntaxe de durée de vie peut être verbeuse, mais elle est essentielle pour la sécurité et la gestion de la mémoire. Les règles d’élision des trois durées de vie fournissent des directives qui permettent à Rust de déduire la durée de vie des références dans certaines situations.
La règle de durée de vie des entrées
La règle de durée de vie des entrées spécifie que si une fonction ou une méthode prend une ou plusieurs références comme paramètres d’entrée, Rust suppose que toutes les références ont la même durée de vie.
En clair, la durée de vie des références en sortie sera la même que celle des références en entrée.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
Dans le le plus long Rust en déduit que la durée de vie de la référence de sortie est la même que celle de la référence d’entrée car elles ont toutes les deux le même paramètre de durée de vie. ‘a.
La règle de durée de vie des entrées facilite l’écriture de fonctions génériques qui prennent plusieurs références en entrée.
La règle de durée de vie en sortie
La règle de durée de vie de la sortie spécifie que si une fonction ou une méthode renvoie une référence, Rust supposera que la durée de vie de la référence de sortie est différente de la durée de vie de toute référence d’entrée.
fn first_word<'a>(s: &'a str) -> &'a str {
s.split_whitespace().next().unwrap()
}
Dans cette fonction, Rust déduit que la durée de vie de la référence de sortie est différente de la durée de vie de la référence d’entrée parce que la fonction split_whitespace() crée une référence de sortie qui ne prend aucun paramètre de référence d’entrée.
La règle de l’élision des durées de vie
La règle d’élision des durées de vie s’applique si une fonction ou une méthode prend une référence ou un paramètre d’entrée et renvoie une référence. Dans ce cas, Rust suppose que la référence de sortie a la même durée de vie que la référence d’entrée.
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
Dans cette fonction, Rust déduit que la durée de vie de la référence de sortie est la même que la durée de vie de la référence d’entrée parce que la référence d’entrée y n’a pas de paramètre de durée de vie. Rust supprime le paramètre de durée de vie pour y et suppose qu’il a la même durée de vie que x.
Cette règle facilite l’écriture de fonctions qui prennent une référence en entrée et renvoient une référence en sortie.
Traits et durées de vie
Vous pouvez combiner les traits et les durées de vie pour créer des fonctions génériques qui fonctionnent pour les types qui implémentent un trait et ont une durée de vie valide.
Voici un trait et une fonction qui fait référence à une valeur qui implémente le trait.
trait ToString {
fn to_string(&self) -> String;
}
fn to_string<'a, T: ToString>(t: &'a T) -> String {
t.to_string()
}
Ici, le paramètre de durée de vie ‘a garantit que la référence t est valable pour la durée de vie de l’objet auquel il fait référence. Vous pouvez utiliser l’option to_string avec les types qui implémentent la fonction ToString Le trait de caractère ayant une durée de vie valide.
Les traits constituent la base de l’implémentation des concepts de la POO en Rust
Les traits permettent de définir des comportements. Bien que Rust ne soit pas un langage de programmation orienté objet (POO), vous pouvez utiliser les traits pour mettre en œuvre les concepts de POO, de l’encapsulation à l’héritage, en passant par le polymorphisme et l’abstraction.
L’implémentation de ces concepts OOP avec les traits rend vos programmes Rust évolutifs, robustes, maintenables et efficaces.
