Die Where-Klausel
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 42 von 61.
Wenn du weitere Trait-Bounds hinzufügst, können Funktionssignaturen schwer lesbar werden. Betrachte eine Funktion mit mehreren generischen Parametern, von denen jeder mehrere Traits erfordert:
fn complex_operation<T: Display + Clone, U: Debug + Summary>(first: T, second: U) {
// ...
}
Das funktioniert, aber die Signatur wird zunehmend überladen. Die where-Klausel bietet eine übersichtlichere Alternative, indem sie die Trait-Grenzen hinter die Parameterliste verschiebt:
fn complex_operation<T, U>(first: T, second: U)
where
T: Display + Clone,
U: Debug + Summary,
{
// ...
}
Beide Versionen sind funktional identisch: Die where-Klausel dient ausschließlich der Lesbarkeit. Die Anforderungen jedes Typs stehen in einer eigenen Zeile, sodass sich leicht erfassen lässt, was jeder generische Parameter benötigt.
Die where-Klausel wird besonders wertvoll, wenn Einschränkungen komplex sind oder wenn du viele generische Parameter hast. Sie hält den Funktionsnamen und die Parameter auf einen Blick sichtbar, während die Einschränkungen darunter separat aufgeführt werden. Dies ist der bevorzugte Stil in den meisten Rust-Codebasen, wenn die Einschränkungen über ein einfaches T: Trait hinausgehen.
Aufgabe
EinfachErstelle ein Datenverarbeitungssystem, das die where-Klausel verwendet, damit komplexe Trait-Bounds lesbar bleiben! Du erstellst eine generische Funktion mit mehreren Typparametern, von denen jeder unterschiedliche Traits erfordert, und organisierst die Bounds übersichtlich mithilfe der where-Syntax.
Du organisierst deinen Code auf zwei Dateien:
processor.rs: Definiere zwei öffentliche Traits und eine generische Funktion, die beide verwendet:Summarize: ein Trait mit einersummary-Methode, die&selfübernimmt und einenStringzurückgibtValidate: ein Trait mit einervalidate-Methode, die&selfübernimmt und einenboolzurückgibt
process_items, die zwei Parameter unterschiedlicher generischer Typen akzeptiert. Verwende einewhere-Klausel, um festzulegen, dass der erste Typ sowohlCloneals auchSummarizeimplementieren muss, während der zweite TypValidateimplementieren muss. Die Funktion soll die Zusammenfassung des ersten Elements ausgeben und anschließend ausgeben, ob das zweite Element gültig ist.main.rs: Erstelle zwei öffentliche Structs, die die erforderlichen Traits implementieren:Article: mit einem öffentlichen Feldtitle(String). ImplementiereSummarize, sodassArticle: {title}zurückgegeben wird, und leiteCloneab.Form: mit einem öffentlichen Feldfilled(bool). ImplementiereValidate, sodass der Wert vonfilledzurückgegeben wird.
process_itemsmit beiden auf.
Die where-Klausel macht deine Funktionssignatur deutlich übersichtlicher, als wenn du alle Bounds in die spitzen Klammern packen würdest. Die Anforderungen jedes Typs erscheinen in einer eigenen Zeile, sodass leicht zu erkennen ist, was jeder generische Parameter benötigt.
Deine Ausgabe sollte die Zusammenfassung gefolgt vom Validierungsergebnis anzeigen:
Article: {title}
Valid: {true/false}Zum Beispiel mit den Eingaben Rust Tips und true:
Article: Rust Tips
Valid: trueUnd mit den Eingaben Breaking News und false:
Article: Breaking News
Valid: falseDu erhältst zwei Eingaben: den Artikeltitel und die Information, ob das Formular ausgefüllt ist (als bool parsen).
Probier es selbst
mod processor;
use processor::{Summarize, Validate, process_items};
use std::io;
// TODO: Definiere eine öffentliche struct Article mit einem öffentlichen title-Feld (String)
// Leite Clone für Article ab
// TODO: Implementiere Summarize für Article
// Die summary-Methode sollte "Article: {title}" zurückgeben
// TODO: Definiere eine öffentliche struct Form mit einem öffentlichen filled-Feld (bool)
// TODO: Implementiere Validate für Form
// Die validate-Methode sollte den Wert von filled zurückgeben
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: Erstelle ein Article mit dem gegebenen title
// TODO: Erstelle ein Form mit dem gegebenen filled-Wert
// TODO: Rufe process_items mit dem article und form auf
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Methoden und Verhalten
Einführung in ImplementierungsblöckeDer Self-ParameterVeränderliche MethodenAssoziierte FunktionenMehrere ImplementierungsblöckeMethod ChainingRückblick – Rechteck-Aktionen4Projekt: Virtuelles Haustier
Das Haustier definierenDas Haustier füttern7Standard-Traits
Das Debug-TraitDas Display-TraitClone und CopyGleichheits-TraitsZusammenfassung – Druckbarer Punkt10Projekt: Dokumentensystem
Das Draw-TraitText-Komponente2Kapselung und Module
Grundlagen der ModuleDas Public-SchlüsselwortPrivate FelderGetterSetterWiederholung – Sicherer Tresor5Generics
Generische StructsGenerische MethodenMehrere generische TypenGenerische FunktionenZusammenfassung - Koordinatenpunkt8Traits als Einschränkungen
Syntax für Trait BoundsMehrfache BoundsDie Where-KlauselTypen mit Traits zurückgebenRückblick – Generischer Drucker11Design Patterns in Rust
Newtype-PatternKompositionDas Drop-TraitFrom und IntoZusammenfassung – Smart Pointer Mock3Fortgeschrittene Enums
Enums mit DatenMethoden für EnumsMatching von DatenvariantenDas Option-Enum erneut betrachtetZusammenfassung – Shape-Enum6Definition von Traits
Was ist ein Trait?Traits implementierenStandardimplementierungenStandardimplementierungen überschreibenTraits mit ParameternZusammenfassung – Media PlayerÜbe selbstständig: Online-Rust-Compiler