Implémentations par défaut
Fait partie de la section Object Oriented Programming du Journey Rust de Coddy. Leçon 31 sur 61.
Jusqu’à présent, chaque méthode de trait que nous avons définie n’était qu’une signature : une exigence sans corps. Mais que se passe-t-il si la plupart des types implémentant un trait utilisent la même logique ? Rust vous permet de fournir une implémentation par défaut directement dans la définition du trait.
Au lieu de terminer la signature de la méthode par un point-virgule, vous ajoutez un corps :
trait Greet {
fn greet(&self) -> String {
String::from("Hello there!")
}
}
Désormais, tout type qui implémente Greet obtient automatiquement cette method, sans écrire de code dans le bloc impl :
struct Guest;
impl Greet for Guest {}
let visitor = Guest;
println!("{}", visitor.greet()); // Bonjour !
Le empty impl Greet for Guest {} est valide, car le trait fournit déjà le comportement. Le struct « adhère » simplement au trait et reçoit gratuitement la méthode default.
Ce modèle est utile lorsque vous souhaitez définir un comportement partagé qui fonctionne dans la plupart des cas. Les types qui ont besoin de quelque chose de différent peuvent remplacer le comportement default (ce que vous apprendrez ensuite), tandis que les types qui se satisfont du comportement standard n’ont pas besoin de se répéter.
Défi
FacileExplorons la puissance des default implementations en construisant un système de notification ! Tu vas créer un trait qui fournit un comportement standard prêt à l'emploi, afin que les types puissent l'adopter sans écrire de code répétitif.
Tu vas organiser ton code sur trois fichiers :
notifiable.rs: définis un traitNotifiablepublic avec une méthodenotify(&self) -> Stringqui possède une default implementation. La valeur par défaut doit retourner"You have a new notification!". Tout type qui implémente ce trait recevra automatiquement ce comportement sans avoir besoin de rien écrire dans son impl block.alerts.rs: crée deux unit structs publics simples qui implémentent ton trait :EmailAlert: implémenteNotifiableavec un impl block vide (utilise la valeur par défaut)SystemAlert: implémente égalementNotifiableavec un impl block vide (utilise la valeur par défaut)
main.rs: rassemble tes modules et montre comment les deux types d'alerte partagent le même comportement par défaut. Crée des instances des deux structs et appelle leurs méthodesnotify.
Dans ton fichier principal, crée un EmailAlert et un SystemAlert, puis affiche leurs notifications sur des lignes séparées.
Ta sortie doit être :
You have a new notification!
You have a new notification!Remarque comme les deux types produisent une sortie identique : c'est toute la beauté des default implementations ! Aucun des deux structs n'a eu besoin d'écrire du code pour la méthode notify, et pourtant tous deux disposent d'un comportement de notification pleinement fonctionnel. Dans la prochaine leçon, tu apprendras à remplacer ces valeurs par défaut lorsqu'un type a besoin d'un comportement personnalisé.
Essayez vous-même
mod notifiable;
mod alerts;
use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};
fn main() {
// TODO: Créer une instance de EmailAlert
// TODO: Créer une instance de SystemAlert
// TODO: Print the notification from EmailAlert
// TODO: Print the notification from SystemAlert
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Méthodes et comportement
Introduction aux blocs d'implémentationLe paramètre SelfMéthodes mutablesFonctions associéesBlocs d'implémentation multiplesChaînage de méthodesRécapitulatif - Actions sur le rectangle4Projet : Animal virtuel
Définir l'animalNourrir l'animal7Traits standards
Le trait DebugLe trait DisplayClone et CopyLes traits d'égalitéRécapitulatif - Point affichable10Projet : Système de documents
Le trait DrawComposant texte2Encapsulation et modules
Bases des modulesLe mot-clé publicChamps privésGettersSettersRécapitulatif - Casier sécurisé5Généricité
Structures génériquesMéthodes génériquesPlusieurs types génériquesFonctions génériquesRécapitulatif - Point de coordonnées8Les Traits comme contraintes
Syntaxe des contraintes de traitContraintes multiplesLa clause whereRetourner des types avec des traitsRécapitulatif - Afficheur générique11Design Patterns en Rust
Le pattern NewtypeCompositionLe trait DropFrom et IntoRécapitulatif - Mock de Smart Pointer3Enums avancées
Enums avec donnéesMéthodes sur les EnumsMatching des variantes de donnéesL'Enum Option revisitéeRécapitulatif - Enum Shape6Définition des Traits
Qu'est-ce qu'un Trait ?Implémenter des TraitsImplémentations par défautRedéfinir les comportements par défautTraits avec paramètresRécapitulatif - Lecteur multimédiaEntraînez-vous par vous-même : Compilateur Rust en ligne