Menu
Coddy logo textTech

Syntaxe des contraintes de trait

Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 40 sur 61.

Jusqu’à présent, vous avez appris à définir des traits et à les implémenter pour des structures. Vous avez également travaillé avec les génériques pour écrire du code flexible. Il est maintenant temps de combiner ces concepts : utiliser les traits pour contraindre les types qu’une fonction générique peut accepter.

Considérez une fonction générique qui doit appeler une méthode spécifique sur son paramètre. Sans aucune contrainte, Rust ne garantit pas que le type T possède cette méthode :

fn print_info<T>(item: T) {
    println!("{}", item.summarize());  // Erreur ! T pourrait ne pas avoir summarize()
}

La solution est une contrainte de trait. En ajoutant : TraitName après le paramètre générique, vous indiquez à Rust que T doit implémenter ce trait :

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());  // Maintenant Rust sait que cette méthode existe
}

La syntaxe <T: Summary> se lit « T est n’importe quel type qui implémente Summary ». Désormais, la fonction n’accepte que les types qui possèdent le comportement requis. Si vous essayez de transmettre un type qui n’implémente pas Summary, le compilateur le refusera avec une erreur claire.

Ce modèle est puissant, car il vous permet d’écrire du code générique qui a tout de même accès à des fonctionnalités spécifiques. Votre fonction reste flexible : elle fonctionne avec n’importe quel type, mais seuls les types qui fournissent le comportement dont vous avez besoin sont acceptés.

challenge icon

Défi

Facile

Construisons un système de notifications qui utilise les contraintes de traits afin de garantir que seuls les messages correctement formatés puissent être envoyés ! Tu vas créer une fonction générique qui accepte tout type implémentant un trait spécifique, afin de montrer comment les contraintes de traits limitent les paramètres génériques.

Tu organiseras ton code sur deux fichiers :

  • notification.rs : définis un trait public Notify avec une méthode appelée message qui prend &self et renvoie une String. Crée ensuite deux structures publiques qui implémentent ce trait :
    • Email : avec un champ public subject (String). Sa méthode message doit renvoyer Email: {subject}
    • SMS : avec un champ public content (String). Sa méthode message doit renvoyer SMS: {content}
    Enfin, crée une fonction générique publique appelée send_notification qui accepte tout type T implémentant le trait Notify. Cette fonction doit afficher le résultat de l'appel à message() sur l'élément.
  • main.rs : importe ton module de notification et utilise les entrées fournies pour créer une instance de Email et une instance de SMS. Appelle send_notification avec chacune d'elles afin de montrer que ta fonction générique fonctionne avec tout type qui implémente Notify.

L'idée essentielle ici est que send_notification ne connaît pas le type concret qu'elle reçoit. Elle sait seulement que ce type peut produire un message. La contrainte de trait T: Notify garantit cette capacité.

Ta sortie doit montrer l'envoi des deux notifications :

Email: {subject}
SMS: {content}

Par exemple, avec les entrées Meeting Tomorrow et On my way! :

Email: Meeting Tomorrow
SMS: On my way!

Tu recevras deux entrées : l'objet de l'e-mail et le contenu du SMS.

Essayez vous-même

mod notification;

use notification::{Email, SMS, send_notification};

fn main() {
    // Lire les entrées
    let mut subject = String::new();
    std::io::stdin().read_line(&mut subject).expect("Failed to read line");
    let subject = subject.trim().to_string();
    
    let mut content = String::new();
    std::io::stdin().read_line(&mut content).expect("Failed to read line");
    let content = content.trim().to_string();
    
    // TODO: Créer une instance Email avec le subject
    
    // TODO: Créer une instance SMS avec le content
    
    // TODO: Appeler send_notification avec l'email
    
    // TODO: Appeler send_notification avec le sms
}
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 Programmation Orientée Objet

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