Traits avec paramètres
Fait partie de la section Object Oriented Programming du Journey Rust de Coddy — leçon 33 sur 61.
Jusqu'à présent, nos méthodes de trait n'ont pris que &self comme paramètre. Mais les méthodes de trait peuvent accepter des paramètres supplémentaires tout comme les méthodes régulières — cela les rend bien plus utiles pour les opérations du monde réel.
Lors de la définition d'un trait, vous pouvez inclure tous les paramètres dont vous avez besoin dans les signatures de méthode :
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
Ce trait Calculator nécessite deux méthodes, chacune acceptant deux entiers en plus de &self. Tout type implémentant ce trait doit fournir les deux opérations avec ces signatures exactes.
Voici comment une structure pourrait l'implémenter :
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
Maintenant, vous pouvez utiliser ces méthodes avec des arguments :
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
Le trait définit quels paramètres chaque méthode accepte, tandis que l'implémentation définit comment ces paramètres sont utilisés. Cela permet à différents types d'effectuer les mêmes opérations à leur manière — peut-être qu'un LoggingCalc pourrait afficher chaque opération avant de renvoyer le résultat.
Défi
FacileConstruisons une boîte à outils de manipulation de chaînes de caractères où différents processeurs peuvent transformer du texte à leur manière ! Vous allez créer un trait TextProcessor avec des méthodes qui acceptent des paramètres, puis l'implémenter pour deux types de processeurs différents.
Vous organiserez votre code sur trois fichiers :
processor.rs: Définissez un trait publicTextProcessoravec deux méthodes qui acceptent des paramètres :repeat(&self, text: &str, times: u32) -> String— répète le texte donné un nombre spécifié de foistruncate(&self, text: &str, max_len: usize) -> String— raccourcit le texte à la longueur maximale spécifiée
processors.rs: Créez deux structures publiques qui implémentent votre trait :SimpleProcessor— une structure unitaire qui répète le texte avec des espaces entre chaque répétition (par exemple, "Hi" répété 3 fois devient "Hi Hi Hi") et tronque en coupant simplement à la longueur maximaleFancyProcessor— une structure unitaire qui répète le texte avec " * " entre les répétitions (par exemple, "Hi" répété 3 fois devient "Hi * Hi * Hi") et tronque en ajoutant "..." si le texte a été raccourci (uniquement si le texte original était plus long que max_len)
main.rs: Rassemblez vos modules et démontrez comment les mêmes méthodes de trait produisent des résultats différents selon le processeur que vous utilisez.
Dans votre fichier principal, créez les deux processeurs et utilisez les entrées pour les tester. Vous recevrez trois entrées : une chaîne de texte, un nombre de répétitions et une longueur maximale.
Affichez quatre lignes montrant le comportement de chaque processeur :
Simple repeat: {result}
Simple truncate: {result}
Fancy repeat: {result}
Fancy truncate: {result}Par exemple, avec les entrées Hello, 3, et 4 :
Simple repeat: Hello Hello Hello
Simple truncate: Hell
Fancy repeat: Hello * Hello * Hello
Fancy truncate: Hell...Remarquez comment les deux processeurs remplissent le même contrat TextProcessor, mais chacun transforme le texte à sa manière unique. Le trait définit quels paramètres les méthodes acceptent, tandis que chaque implémentation décide comment les utiliser !
Vous recevrez trois entrées : le texte à traiter, le nombre de répétitions (à analyser comme u32), et la longueur maximale pour la troncature (à analyser comme usize).
Aide-mémoire
Les méthodes de trait peuvent accepter des paramètres supplémentaires au-delà de &self :
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32;
fn multiply(&self, a: i32, b: i32) -> i32;
}
Implémentation d'un trait avec des paramètres :
struct BasicCalc;
impl Calculator for BasicCalc {
fn add(&self, a: i32, b: i32) -> i32 {
a + b
}
fn multiply(&self, a: i32, b: i32) -> i32 {
a * b
}
}
Utilisation des méthodes de trait avec des arguments :
let calc = BasicCalc;
println!("{}", calc.add(5, 3)); // 8
println!("{}", calc.multiply(4, 7)); // 28
Le trait définit quels paramètres chaque méthode accepte, tandis que l'implémentation définit comment ces paramètres sont utilisés.
Essayez vous-même
mod processor;
mod processors;
use processor::TextProcessor;
use processors::{SimpleProcessor, FancyProcessor};
fn main() {
// Lire les entrées
let mut text = String::new();
std::io::stdin().read_line(&mut text).expect("Failed to read line");
let text = text.trim();
let mut times_input = String::new();
std::io::stdin().read_line(&mut times_input).expect("Failed to read line");
let times: u32 = times_input.trim().parse().expect("Failed to parse times");
let mut max_len_input = String::new();
std::io::stdin().read_line(&mut max_len_input).expect("Failed to read line");
let max_len: usize = max_len_input.trim().parse().expect("Failed to parse max_len");
// Créer les processeurs
let simple = SimpleProcessor;
let fancy = FancyProcessor;
// TODO: Utiliser les processeurs pour transformer le texte et afficher les résultats
// Afficher quatre lignes :
// Simple repeat : {result}
// Simple truncate : {result}
// Fancy repeat : {result}
// Fancy truncate : {result}
}
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édia