Menu
Coddy logo textTech

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 { ... } }
// }
challenge icon

Défi

Facile

Construisons 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 trait Greet public avec une method greet qui prend &self et renvoie une String. Crée ensuite une struct Card (elle n’a pas besoin d’être public !) avec un champ message (String). Implement le trait Greet pour Card, en renvoyant le message. Enfin, crée une function public appelée create_greeting qui prend un String parameter et renvoie impl Greet. Cette function doit créer et renvoyer une Card avec le message given.
  • main.rs : Importe ton module greetings et utilise l’input fourni pour appeler create_greeting. L’intérêt est que ton fichier main ne connaît pas du tout le type Card. Il sait only qu’il a reçu quelque chose qui implements Greet. Appelle la greet method 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
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur Rust en ligne