Menu
Coddy logo textTech

Iterieren über Trait-Objekte

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

Da Sie nun verschiedene Typen in einem Vec<Box<dyn Trait>> speichern können, ist der nächste Schritt die Verarbeitung dieser Sammlung. Das Iterieren über Trait-Objekte funktioniert genau wie das Iterieren über jeden anderen Vektor – Sie durchlaufen jedes Element in einer Schleife und rufen die durch den 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 Sie mit for shape in &shapes iterieren, ist jedes shape eine Referenz auf ein Box<dyn Shape>. Sie können jede Methode des Shape-Traits direkt aufrufen – Rust übernimmt den dynamischen Dispatch automatisch und ruft die korrekte Implementierung für jeden konkreten Typ auf.

Dieses Muster ist leistungsstark für das Aggregieren von Ergebnissen. Sie könnten alle Flächen summieren, Formen nach Größe filtern oder die Sammlung transformieren. Die entscheidende Erkenntnis ist, dass Sie, sobald sich Elemente in einer Trait-Objekt-Sammlung befinden, rein über die Schnittstelle des Traits mit ihnen interagieren, unabhängig von ihren zugrunde liegenden Typen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns einen Preisrechner für einen Warenkorb erstellen, der verschiedene Arten von Artikeln enthalten kann! Sie werden ein System erstellen, in dem Produkte und Dienstleistungen – jeweils mit unterschiedlicher Preislogik – zusammen gespeichert und verarbeitet werden können, um einen Gesamtpreis zu berechnen.

Sie werden Ihren Code auf zwei Dateien verteilen:

  • items.rs: Definieren Sie ein öffentliches Priceable-Trait mit einer price-Methode, die &self entgegennimmt und ein f64 zurückgibt. Erstellen Sie dann zwei öffentliche Structs:
    • Product — mit den öffentlichen Feldern name (String) und cost (f64). Die Methode price sollte einfach den cost zurückgeben.
    • Service — mit den öffentlichen Feldern name (String), hourly_rate (f64) und hours (f64). Die Methode price sollte hourly_rate * hours zurückgeben.
  • main.rs: Binden Sie Ihr items-Modul ein und erstellen Sie einen Vektor vom Typ Vec<Box<dyn Priceable>>. Verwenden Sie die Eingaben, um ein Product und ein Service zu erstellen, fügen Sie diese Ihrem Vektor hinzu und iterieren Sie dann durch die Sammlung, um den Gesamtpreis aller Artikel zu berechnen und auszugeben.

Die Stärke von Trait-Objekten zeigt sich hier – Ihr Iterationscode muss nicht wissen, ob er ein Product oder ein Service verarbeitet. Er ruft einfach price() für jedes Element auf, und Rust leitet den Aufruf automatisch an die richtige Implementierung weiter.

Ihre Ausgabe sollte den Gesamtpreis mit einer Dezimalstelle anzeigen:

Total: ${total}

Zum Beispiel mit den Eingaben Laptop, 999.99, Consulting, 150.0 und 3.0:

Total: $1449.99

Und mit den Eingaben Book, 29.99, Tutoring, 50.0 und 2.0:

Total: $129.99

Sie erhalten fünf Eingaben: den Produktnamen, die Produktkosten, den Dienstleistungsnamen, den Stundensatz und die geleisteten Arbeitsstunden (parsen Sie numerische Werte als f64).

Spickzettel

Um über eine Sammlung von Trait-Objekten zu iterieren, verwenden Sie eine standardmäßige for-Schleife mit einer Referenz auf den Vektor:

for item in &collection {
    item.trait_method();
}

Beim Iterieren mit for item in &vec ist jedes item eine Referenz auf ein Box<dyn Trait>. Sie können jede Methode des Traits direkt aufrufen – Rust übernimmt den dynamischen Dispatch automatisch und ruft die korrekte Implementierung für jeden konkreten Typ auf.

Beispiel für das Iterieren über Trait-Objekte, um Ergebnisse zu aggregieren:

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 }),
    ];

    for shape in &shapes {
        println!("Area: {}", shape.area());
    }
}

Dieses Muster ist nützlich, um Ergebnisse zu aggregieren – Sie können Werte summieren, nach Kriterien filtern oder die Sammlung transformieren, während Sie mit den Elementen rein über die Schnittstelle des Traits interagieren.

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 unter Verwendung von product_name und product_cost

    // TODO: Erstelle einen Service unter Verwendung von 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 Nachkommastelle aus
    // Format: Total: ${total:.1}
}
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