Menu
Coddy logo textTech

Mehrfache Bounds

Teil des Abschnitts Object Oriented Programming der Rust-Journey von Coddy — Lektion 41 von 61.

Manchmal reicht eine einzelne Trait-Begrenzung nicht aus. Möglicherweise benötigen Sie einen generischen Typ, der sowohl ausgegeben werden kann als auch eine Zusammenfassung bereitstellt. Rust ermöglicht es Ihnen, mehrere Traits mithilfe der +-Syntax zu fordern.

So geben Sie an, dass ein Typ zwei Traits implementieren muss:

use std::fmt::Display;

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

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

Die Einschränkung T: Display + Summary bedeutet: „T muss sowohl Display als auch Summary implementieren“. Innerhalb der Funktion kannst du Funktionen beider Traits nutzen – das Drucken mit {} (von Display) und das Aufrufen von summarize() (von Summary).

Sie können so viele Traits wie nötig verketten:

fn process<T: Display + Summary + Clone>(item: T) {
    // Kann drucken, zusammenfassen UND klonen
}

Dieses Muster ist essenziell, wenn Ihre Funktion auf mehreren Verhaltensweisen basiert. Anstatt jeden beliebigen Typ zu akzeptieren und zu hoffen, dass es funktioniert, deklarieren Sie explizit genau, welche Fähigkeiten erforderlich sind – und der Compiler erzwingt dies zur Kompilierzeit.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Produktinspektionssystem bauen, das erfordert, dass Artikel über mehrere Fähigkeiten verfügen! Sie werden eine generische Funktion erstellen, die nur Typen akzeptiert, die sowohl ein benutzerdefiniertes Trait als auch ein Standard-Trait implementieren, und so demonstrieren, wie die +-Syntax mehrere Bounds kombiniert.

Sie werden Ihren Code auf zwei Dateien aufteilen:

  • product.rs: Definieren Sie ein öffentliches Inspectable-Trait mit einer Methode namens inspect, die &self entgegennimmt und einen String mit Inspektionsdetails zurückgibt. Erstellen Sie dann eine öffentliche Gadget-Struktur mit den öffentlichen Feldern name (String) und serial (u32). Ihr Gadget sollte sowohl Inspectable (Rückgabe von "Inspecting: {}") als auch std::fmt::Display (Formatierung als "{} (SN: {})") implementieren. Erstellen Sie schließlich eine öffentliche generische Funktion namens full_report, die jeden Typ T akzeptiert, der sowohl Display als auch Inspectable implementiert. Diese Funktion sollte zwei Zeilen ausgeben: zuerst das Element unter Verwendung des {}-Formatierers, dann das Ergebnis des Aufrufs von inspect().
  • main.rs: Binden Sie Ihr Produktmodul ein und erstellen Sie eine Gadget-Instanz unter Verwendung der bereitgestellten Eingaben. Rufen Sie full_report mit Ihrem Gadget auf, um zu zeigen, dass es beide Trait-Anforderungen erfüllt.

Die Stärke von multiplen Bounds liegt darin, dass Ihre full_report-Funktion Fähigkeiten aus beiden Traits nutzen kann – die ansprechende Anzeige des Elements UND das Abrufen von Inspektionsdetails – alles garantiert zur Kompilierzeit.

Ihre Ausgabe sollte sowohl das Anzeigeformat als auch das Inspektionsergebnis zeigen:

{name} (SN: {serial})
Inspecting: {name}

Zum Beispiel mit den Eingaben Smartwatch und 98765:

Smartwatch (SN: 98765)
Inspecting: Smartwatch

Sie erhalten zwei Eingaben: den Namen des Gadgets und die Seriennummer (parsen als u32).

Spickzettel

Sie können mehrere Trait-Bounds für einen generischen Typ mit der Syntax + anfordern:

fn function_name<T: Trait1 + Trait2>(item: T) {
    // Kann Fähigkeiten beider Traits nutzen
}

Beispiel mit Display und einem benutzerdefinierten Trait:

use std::fmt::Display;

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

fn announce<T: Display + Summary>(item: T) {
    println!("Breaking news: {}", item);
    println!("Summary: {}", item.summarize());
}

Sie können bei Bedarf mehrere Traits verketten:

fn process<T: Display + Summary + Clone>(item: T) {
    // Kann drucken, zusammenfassen UND klonen
}

Der Compiler erzwingt zur Kompilierzeit, dass der Typ alle angegebenen Traits implementiert.

Probier es selbst

mod product;

use product::{Gadget, full_report};

fn main() {
    // Eingaben lesen
    let mut name = String::new();
    std::io::stdin().read_line(&mut name).expect("Failed to read line");
    let name = name.trim().to_string();
    
    let mut serial_input = String::new();
    std::io::stdin().read_line(&mut serial_input).expect("Failed to read line");
    let serial: u32 = serial_input.trim().parse().expect("Failed to parse serial");
    
    // TODO: Erstelle eine Gadget-Instanz mit dem Namen und der Seriennummer
    
    // TODO: Rufe full_report mit deinem Gadget auf
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Object Oriented Programming