La clause where
Fait partie de la section Programmation Orientée Objet du Journey Rust de Coddy. Leçon 42 sur 61.
À mesure que vous ajoutez davantage de contraintes de traits, les signatures de fonctions peuvent devenir difficiles à lire. Considérez une fonction comportant plusieurs paramètres génériques, chacun nécessitant plusieurs traits :
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
Cela fonctionne, mais la signature devient chargée. La clause where offre une alternative plus claire en plaçant les contraintes de trait après la liste des paramètres :
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Les deux versions sont fonctionnellement identiques : la clause where concerne uniquement la lisibilité. Les exigences de chaque type apparaissent sur leur propre ligne, ce qui permet de parcourir facilement les besoins de chaque paramètre générique.
La clause where devient particulièrement utile lorsque les contraintes sont complexes ou lorsque vous avez de nombreux paramètres génériques. Elle permet de garder le nom et les paramètres de la fonction visibles en un coup d’œil, les contraintes étant répertoriées séparément ci-dessous. C’est le style privilégié dans la plupart des bases de code Rust lorsque les contraintes dépassent un simple T: Trait.
Défi
FacileConstruisons un système de traitement des données qui utilise la clause where afin de garder lisibles les contraintes de traits complexes ! Vous allez créer une fonction générique avec plusieurs paramètres de type, chacun nécessitant des traits différents, et organiser proprement les contraintes à l’aide de la syntaxe where.
Vous allez organiser votre code sur deux fichiers :
processor.rs: définissez deux traits publics et une fonction générique qui les utilise tous les deux :Summarize: un trait avec une méthodesummaryqui prend&selfet renvoie uneStringValidate: un trait avec une méthodevalidatequi prend&selfet renvoie unbool
process_itemsqui accepte deux paramètres de types génériques différents. Utilisez une clausewherepour spécifier que le premier type doit implémenter à la foisCloneetSummarize, tandis que le second type doit implémenterValidate. La fonction doit afficher le résumé du premier élément, puis indiquer si le second élément est valide.main.rs: créez deux structures publiques qui implémentent les traits requis :Article: avec un champ publictitle(String). ImplémentezSummarizepour renvoyerArticle: {title}, et dérivezClone.Form: avec un champ publicfilled(bool). ImplémentezValidatepour renvoyer la valeur defilled.
process_itemsavec les deux.
La clause where rend la signature de votre fonction beaucoup plus claire que si toutes les contraintes étaient entassées entre les chevrons. Les exigences de chaque type apparaissent sur leur propre ligne, ce qui permet de voir facilement ce dont chaque paramètre générique a besoin.
Votre sortie doit afficher le résumé suivi du résultat de la validation :
Article: {title}
Valid: {true/false}Par exemple, avec les entrées Rust Tips et true :
Article: Rust Tips
Valid: trueEt avec les entrées Breaking News et false :
Article: Breaking News
Valid: falseVous recevrez deux entrées : le titre de l’article et si le formulaire est rempli (à analyser comme un bool).
Essayez vous-même
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Définir une struct publique Article avec un champ public title (String)
// Dériver Clone pour Article
// TODO: Implémenter Summarize pour Article
// La méthode summary doit retourner "Article: {title}"
// TODO: Définir une struct publique Form avec un champ public filled (bool)
// TODO: Implémenter Validate pour Form
// La méthode validate doit retourner la valeur de filled
fn main() {
let mut title = String::new();
io::stdin().read_line(&mut title).expect("Failed to read line");
let title = title.trim().to_string();
let mut filled_input = String::new();
io::stdin().read_line(&mut filled_input).expect("Failed to read line");
let filled: bool = filled_input.trim().parse().expect("Failed to parse bool");
// TODO: Créer un Article avec le title donné
// TODO: Créer un Form avec la valeur filled donnée
// TODO: Appeler process_items avec l'article et le form
}
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
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