Einführung
Unterschiedliche Datentypen müssen manchmal dasselbe Verhalten zusagen. Ein Gewächshaus-Sensor und eine Sendung haben unterschiedliche Felder, können aber beide ihren aktuellen Status beschreiben. Rust-Traits benennen diese gemeinsame Fähigkeit, ohne die Datentypen auf dieselbe Datenstruktur festzulegen.
In diesem Lab definieren Sie ein kleines Trait, implementieren es für zwei vorbereitete Strukturen und übergeben beide Werte an eine generische Reporting-Funktion. Die längeren Strukturen und Beispieldaten sind bereits erstellt, damit Sie sich bei jeder Änderung auf das Wesentliche konzentrieren können.
Einen gemeinsamen Statusvertrag definieren
In diesem Schritt definieren Sie ein Trait, das ein Verhalten benennt, das ansonsten unabhängige Typen gemeinsam haben.
Wechseln Sie in das vorbereitete Paket und sehen Sie sich das vollständige Grundgerüst an. Der Befehl sed -n gibt die angeforderten Zeilen aus, ohne sie zu bearbeiten:
cd /home/labex/project/status-reporter
sed -n '1,160p' src/main.rs
Sensor und Shipment haben unterschiedliche Felder. Ihre vorbereiteten Textfelder verwenden &'static str. Dabei handelt es sich um geliehene String-Literale, die während der gesamten Programmlaufzeit gültig bleiben. Die Lebensdauerannotation 'static gehört nicht zu dieser Trait-Lektion und wird absichtlich auf später verschoben. Sie müssen sie hier weder bearbeiten noch inhaltlich nachvollziehen.
Unterhalb der Platzhalter ist show_status bereits generisch. Lesen Sie T: Status in drei Teilen: T steht für einen konkreten Typ, der Doppelpunkt bedeutet „muss erfüllen“, und Status ist das erforderliche Trait. In einfachen Worten: „für jeden Typ T, sofern T Status implementiert“. Diese Zusage erlaubt es der Funktion, sicher item.status() aufzurufen.
Das Trait muss vorhanden sein, bevor Rust den Bound verstehen kann. Öffnen Sie den Quellcode:
nano src/main.rs
Ersetzen Sie // STATUS_TRAIT durch:
trait Status {
fn status(&self) -> String;
}
Die Methodenzeile endet mit einem Semikolon, weil das Trait eine erforderliche Methode deklariert, aber keine Implementierung vorgibt. Mit &self kann die Methode einen Wert untersuchen, ohne dessen Besitz zu übernehmen. String ermöglicht es jedem Typ, seinen eigenen Statustext zu erstellen. Jede spätere Implementierung muss exakt denselben Methodennamen, Empfänger und Rückgabetyp wiederholen und anschließend ihren eigenen Methodenkörper bereitstellen.
Speichern Sie mit Ctrl+O, drücken Sie Enter und beenden Sie Nano mit Ctrl+X. Prüfen Sie anschließend das Paket:
cargo check
Möglicherweise sehen Sie Warnungen, dass das Trait und die vorbereiteten Elemente noch nicht verwendet werden. Die abschließende Zeile Finished ist der wichtige Nachweis: Der gemeinsame Vertrag und der generische Bound sind gültig. In den nächsten beiden Schritten fügen Sie die Implementierungen und Aufrufe hinzu.
Status für Sensor implementieren
In diesem Schritt erfüllen Sie den Trait-Vertrag für Sensor und übergeben einen Sensor an den gemeinsamen Reporter.
Ein Block der Form impl Trait for Type verbindet ein vorhandenes Trait mit einem konkreten Typ. Seine Methodensignatur muss mit dem Trait übereinstimmen, während der Methodenkörper die eigenen Felder dieses Typs verwenden darf.
Öffnen Sie den Quellcode:
nano src/main.rs
Ersetzen Sie // SENSOR_IMPLEMENTATION durch diese kurze Implementierung:
impl Status for Sensor {
fn status(&self) -> String {
format!("{}: {}°C", self.location, self.celsius)
}
}
format! erstellt und gibt einen String zurück; die Funktion gibt selbst nichts aus. Die Implementierung kann location und celsius verwenden, weil self ein geliehener Sensor ist.
Ersetzen Sie als Nächstes // SENSOR_REPORT innerhalb von main durch:
show_status("Sensor", &sensor);
Das Argument &sensor passt zum geliehenen Parameter &T des Reporters. Deshalb bleibt sensor im Besitz von main. Speichern Sie die Datei, beenden Sie Nano und führen Sie anschließend das Programm aus:
cargo run --quiet
Sensor: Greenhouse: 24°C
Diese Zeile zeigt, dass Sensor Status erfüllt und an den generischen Reporter mit Trait-Bound übergeben werden kann. Die Sendung ist noch nicht angebunden.
Status für Shipment implementieren
In diesem Schritt implementieren Sie dasselbe Trait für Shipment und zeigen, dass ein Reporter beide konkreten Typen akzeptiert.
Öffnen Sie den Quellcode erneut:
nano src/main.rs
Ersetzen Sie // SHIPMENT_IMPLEMENTATION durch:
impl Status for Shipment {
fn status(&self) -> String {
format!("#{} {}", self.id, self.stage)
}
}
Diese Methode folgt demselben Trait-Vertrag, verwendet aber die eigenen Felder id und stage der Sendung. Ersetzen Sie anschließend // SHIPMENT_REPORT durch:
show_status("Shipment", &shipment);
Speichern Sie die Datei und beenden Sie Nano. Führen Sie das vollständige Paket aus:
cargo run --quiet
Sensor: Greenhouse: 24°C
Shipment: #204 in transit
Beide Zeilen stammen aus show_status<T: Status>. Der Bound beschreibt das Verhalten, das die Funktion benötigt, während jede Implementierung festlegt, wie ihr eigener Wert dieses Verhalten bereitstellt.
Zusammenfassung
Sie haben ein gemeinsames Verhalten mit einem Trait definiert, es unabhängig für zwei Strukturen implementiert und einen einfachen Trait-Bound verwendet, damit ein generischer Reporter beide Typen akzeptieren kann.


