Retourner des types avec des traits
Fait partie de la section Object Oriented Programming du Journey Rust de Coddy. Leçon 43 sur 61.
Vous avez utilisé des bornes de trait pour limiter les types qu'une function peut accepter. Mais qu'en est-il de ce qu'une function retourne ? La syntaxe impl Trait vous permet de spécifier qu'une function retourne « un type qui implements ce trait » sans nommer le type concrete.
Voici la syntaxe en action :
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
headline: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
self.headline.clone()
}
}
fn create_summary() -> impl Summary {
Article { headline: String::from("Breaking News!") }
}
Le type de retour impl Summary indique aux appelants « vous obtiendrez quelque chose qui implémente Summary ». L’appelant peut utiliser n’importe quelle méthode du trait, mais n’a pas besoin de savoir que le type réel est Article.
Cela est particulièrement utile lorsque le type concrete est complexe ou lorsque vous souhaitez masquer les détails d’implémentation. La function garantit une capacité (le trait), et non un type spécifique.
Cependant, il existe une limitation importante : la function doit return exactement un seul type concrete. Vous ne pouvez pas return conditionnellement différents types qui implémentent le même trait. Cela nécessite des objets de trait, que vous apprendrez à utiliser plus tard.
// Ceci fonctionne - retourne toujours Article
fn make_item() -> impl Summary {
Article { headline: String::from("Hello") }
}
// Ceci ne compilerait PAS - deux types différents
// fn make_item(flag: bool) -> impl Summary {
// if flag { Article { ... } } else { Tweet { ... } }
// }
Défi
FacileConstruisons une fabrique de cartes de vœux qui utilise le type de retour impl Trait pour masquer les détails d’implémentation ! Tu vas créer une function qui renvoie « quelque chose qui peut saluer » sans révéler le type concrete à l’appelant.
Tu vas organiser ton code sur deux fichiers :
greetings.rs: Define un traitGreetpublic avec unemethodgreetqui prend&selfet renvoie uneString. Crée ensuite une structCard(elle n’a pas besoin d’être public !) avec un champmessage(String). Implement le traitGreetpourCard, en renvoyant le message. Enfin, crée une function public appeléecreate_greetingqui prend unStringparameter et renvoieimpl Greet. Cette function doit créer et renvoyer uneCardavec le message given.main.rs: Importe ton module greetings et utilise l’input fourni pour appelercreate_greeting. L’intérêt est que ton fichier main ne connaît pas du tout le typeCard. Il sait only qu’il a reçu quelque chose qui implementsGreet. Appelle lagreetmethod sur la valeur renvoyée et affiche le result.
L’idée essentielle est que Card reste privé au module, mais que les appelants puissent tout de même l’utiliser via l’interface du trait. La function promet une capacité (greeting), et non un type spécifique.
Ton résultat doit afficher le greeting :
{message}Par exemple, avec l’input Happy Birthday! :
Happy Birthday!Et avec l’input Congratulations on your promotion! :
Congratulations on your promotion!Tu recevras un input : le message de greeting.
Essayez vous-même
mod greetings;
use greetings::Greet;
fn main() {
// Lire le message de salutation depuis l'entrée
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("Failed to read line");
let message = input.trim().to_string();
// TODO: Appeler create_greeting avec le message
// Note: Vous ne connaissez pas le type concret - seulement qu'il implémente Greet!
// TODO: Appeler la méthode greet et afficher le résultat
}
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