Iterieren über Trait-Objekte
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 47 von 61.
Da du jetzt verschiedene Typen in einem Vec<Box<dyn Trait>> speichern kannst, besteht der nächste Schritt darin, diese Sammlung zu verarbeiten. Das Iterieren über Trait-Objekte funktioniert genauso wie das Iterieren über jeden Vektor. Du durchläufst jedes Element und rufst die vom Trait definierten Methoden auf.
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}
impl Shape for Rectangle {
fn area(&self) -> f64 { self.width * self.height }
}
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
Box::new(Circle { radius: 1.0 }),
];
for shape in &shapes {
println!("Area: {}", shape.area());
}
}
Wenn du mit for shape in &shapes iterierst, ist jedes shape eine Referenz auf ein Box<dyn Shape>. Du kannst jede Methode des Shape-Traits direkt aufrufen: Rust übernimmt den dynamischen Dispatch automatisch und ruft für jeden konkreten Typ die richtige Implementierung auf.
Dieses Muster ist leistungsstark zum Aggregieren von Ergebnissen. Du könntest alle Flächen summieren, Formen nach Größe filtern oder die Sammlung transformieren. Die wichtigste Erkenntnis ist, dass du, sobald sich Elemente in einer Trait-Objektsammlung befinden, ausschließlich über die Schnittstelle des Traits mit ihnen interagierst – unabhängig von ihren zugrunde liegenden Typen.
Aufgabe
EinfachErstellen wir einen Preisrechner für einen Einkaufswagen, der verschiedene Arten von Artikeln aufnehmen kann! Du erstellst ein System, in dem Produkte und Dienstleistungen mit jeweils unterschiedlicher Preislogik gemeinsam gespeichert und verarbeitet werden können, um einen Gesamtpreis zu berechnen.
Du organisierst deinen Code auf zwei Dateien verteilt:
items.rs: Definiere ein öffentlichesPriceable-Trait mit einerprice-Methode, die&selfübernimmt und einf64zurückgibt. Erstelle anschließend zwei öffentliche Structs:Product: mit öffentlichen Feldernname(String) undcost(f64). Die Methodepricesoll einfachcostzurückgeben.Service: mit öffentlichen Feldernname(String),hourly_rate(f64) undhours(f64). Die Methodepricesollhourly_rate * hourszurückgeben.
main.rs: Binde dein items-Modul ein und erstelle einen Vektor vom TypVec<Box<dyn Priceable>>. Verwende die Eingaben, um ein Product und einen Service zu erstellen, füge sie deinem Vektor hinzu und durchlaufe anschließend die Sammlung, um den Gesamtpreis aller Artikel zu berechnen und auszugeben.
Hier zeigt sich die Stärke von Trait-Objekten. Dein Iterationscode muss nicht wissen, ob er ein Product oder einen Service verarbeitet. Er ruft einfach für jedes Element price() auf, und Rust wählt automatisch die passende Implementierung aus.
Deine Ausgabe sollte den Gesamtpreis mit zwei Dezimalstellen anzeigen:
Total: ${total}Beispielsweise mit den Eingaben Laptop, 999.99, Consulting, 150.0 und 3.0:
Total: $1449.99Und mit den Eingaben Book, 29.99, Tutoring, 50.0 und 2.0:
Total: $129.99Du erhältst fünf Eingaben: den Produktnamen, die Produktkosten, den Namen der Dienstleistung, den Stundensatz und die geleisteten Stunden (numerische Werte als f64 parsen).
Probier es selbst
mod items;
use items::{Priceable, Product, Service};
fn main() {
// Eingaben lesen
let mut product_name = String::new();
std::io::stdin().read_line(&mut product_name).expect("Failed to read line");
let product_name = product_name.trim().to_string();
let mut product_cost = String::new();
std::io::stdin().read_line(&mut product_cost).expect("Failed to read line");
let product_cost: f64 = product_cost.trim().parse().expect("Failed to parse");
let mut service_name = String::new();
std::io::stdin().read_line(&mut service_name).expect("Failed to read line");
let service_name = service_name.trim().to_string();
let mut hourly_rate = String::new();
std::io::stdin().read_line(&mut hourly_rate).expect("Failed to read line");
let hourly_rate: f64 = hourly_rate.trim().parse().expect("Failed to parse");
let mut hours = String::new();
std::io::stdin().read_line(&mut hours).expect("Failed to read line");
let hours: f64 = hours.trim().parse().expect("Failed to parse");
// TODO: Erstelle ein Product mit product_name und product_cost
// TODO: Erstelle einen Service mit service_name, hourly_rate und hours
// TODO: Erstelle einen Vec<Box<dyn Priceable>> und füge beide Elemente hinzu
// TODO: Iteriere durch den Vektor und berechne den Gesamtpreis
// TODO: Gib die Gesamtsumme mit einer Dezimalstelle aus
// Format: Total: ${total:.1}
}
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 Player9Trait-Objekte
Dynamischer DispatchVektoren von TraitsIterieren über Trait-ObjekteRückblick – Zoo-ManagerÜbe selbstständig: Online-Rust-Compiler