En savoir plus sur ces deux concepts vous aidera à renforcer votre compréhension du fonctionnement de Rust et de la manière dont vous pouvez implémenter des fonctionnalités OOP.

Les traits et les durées de vie sont des éléments clés de Rust. Vous pouvez utiliser des traits pour définir des comportements et des capacités pour les types à implémenter. Ils sont très polyvalents, vous permettant 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 de la portée. Cela empêche les pointeurs pendants lors de la désallocation de variable.

Ensemble, les traits et les durées de vie contribuent à assurer la sécurité du type, la sécurité de la mémoire et la fiabilité du code.

Comprendre les traits de Rust

Les traits sont des collections de méthodes que d'autres types peuvent implémenter. Les traits sont similaires à interfaces dans des langages comme Java, Go et TypeScript, mais plus flexibles.

Vous utiliserez le trait mot-clé pour définir les traits dans Rust, suivi d'une déclaration des signatures de méthode.

traitMon trait {
fnma_méthode(&soi);
}

Le code définit un trait nommé Mon trait avec un ma_méthode méthode. Le &soi Le paramètre 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 vous pouvez implémenter un trait pour vos types de structure.

structurePersonne {
nom: Chaîne,
âge: u32,
}

mettre en œuvre Info pour Personne {
fnrésumé(&soi) {
imprimez !("Je m'appelle {} et j'ai {} ans.", soi.nom, soi.âge);
}
}

Le Personne structure implémente Info, et vous pouvez appeler le résumé méthode sur les instances de Personne structure.

fnprincipal(){
laisser john = Personne { nom: Chaîne::depuis("John"), âge: 30 };
john.summary(); // Sortie: Je m'appelle John et j'ai 30 ans.
}

Le John variable est une instance de Personne structure.

Le principal appels de fonction résumé qui imprime un message sur la console :

Les énumérations peuvent implémenter des traits. Voici comment vous pouvez définir une énumération avec des variantes qui implémentent le résumé méthode:

énumérationMonEnum {
VarianteA,
VarianteB,
}

mettre en œuvre Info pour MonEnum {
fnrésumé(&soi) {
correspondresoi {
MonEnum:: VarianteA => {
// implémentation pour VarianteA
}
MonEnum:: VarianteB => {
// implémentation pour VariantB
}
}
}
}

Utilisation de caractéristiques 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 Info.

fnfaire quelque chose(valeur: T) {
value.summary();
}

Le la syntaxe spécifie que J doit mettre en œuvre Info. Vous pouvez appeler le résumé fonction avec n'importe quelle valeur qui implémente Info.

Des vies à Rust

L'outil de vérification d'emprunt de Rust analyse les programmes et garantit une utilisation correcte de la mémoire. A Rouille, chaque valeur a un propriétaire qui est responsable de la désallocation de la valeur. Quand variables emprunter des valeurs, ils empruntent une référence à la valeur transmise, 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.

Dans Rust, vous pouvez spécifier une durée de vie en utilisant une annotation apostrophe :

fonction<'un>

Lors de la création d'une référence, la référence 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 garantir que la valeur est valide lorsque la fonction revient.

Voici un exemple de spécification de durée de vie dans une fonction.

fnfaire quelque chose<'un>(x: &'uni32) -> &'uni32 {
X
}

fnprincipal() {
laisser x = 42;
laisser résultat = faire_quelquechose(&x);
imprimez !("Le résultat est: {}", résultat);
}

Dans le faire quelque chose fonction, la 'un Le paramètre de durée de vie indique que la référence à X est valide tant que l'appel de la fonction. La référence renvoyée est également valide tant que la fonction est appelée.

Le principal fonction imprime le résultat en passant une référence à la X variables dans le principal fonction à la console.

La syntaxe de durée de vie peut être détaillée, mais elle est essentielle pour la sécurité et la gestion de la mémoire. Les règles d'élision à trois vies 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 d'entrée 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 termes simples, la durée de vie des références de sortie sera la même que celle des références d'entrée.

fnle plus long<'un>(x: &'unchaîne, y: &'unchaîne) -> &'unchaîne {
si x.len() > y.len() { x } autre { y }
}

Dans le le plus long fonction, Rust déduit que la durée de vie de la référence de sortie est la même que la référence d'entrée car elles ont toutes deux le même paramètre de durée de vie 'un.

La règle de durée de vie des entrées facilite l'écriture de fonctions génériques prenant plusieurs références en entrée.

La règle de durée de vie de la sortie

La règle de durée de vie de 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.

fnpremier mot<'un>(s: &'unchaîne) -> &'unchaîne {
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 car la split_whitespace() La méthode 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'élimination de la durée 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.

fnle plus long<'un>(x: &'unchaîne, y: &chaîne) -> &'unchaîne {
si x.len() > y.len() { x } autre { 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 car la référence d'entrée y n'a pas de paramètre de durée de vie. Rust élide 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 d'entrée et renvoient une référence de sortie.

Traits et durées de vie

Vous pouvez combiner des traits et des 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.

traitToString {
fnto_string(&soi) -> Chaîne;
}

fnto_string<'un, T: ToString>(t: &'un T) -> Chaîne {
t.to_string()
}

Ici, le paramètre de durée de vie 'un s'assure que la référence t est valide pour la durée de vie de l'objet auquel il fait référence. Vous pouvez utiliser le to_string fonctionnent avec des types qui implémentent ToString trait ayant une durée de vie valide.

Les traits constituent la base de l'implémentation des concepts OOP dans Rust

Les traits vous permettent de définir des comportements. Bien que Rust ne soit pas un langage de programmation orienté objet (POO), vous pouvez utiliser des traits pour implémenter des concepts de POO, de l'encapsulation à l'héritage, au polymorphisme et à l'abstraction.

La mise en œuvre de ces concepts OOP avec des traits rend vos programmes Rust évolutifs, robustes, maintenables et efficaces.