Menu
Coddy logo textTech

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.

challenge icon

Aufgabe

Einfach

Erstellen 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 öffentliches Priceable-Trait mit einer price-Methode, die &self übernimmt und ein f64 zurückgibt. Erstelle anschließend zwei öffentliche Structs:
    • Product: mit öffentlichen Feldern name (String) und cost (f64). Die Methode price soll einfach cost zurückgeben.
    • Service: mit öffentlichen Feldern name (String), hourly_rate (f64) und hours (f64). Die Methode price soll hourly_rate * hours zurückgeben.
  • main.rs: Binde dein items-Modul ein und erstelle einen Vektor vom Typ Vec<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.99

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

Total: $129.99

Du 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}
}
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 Objektorientierte Programmierung

Übe selbstständig: Online-Rust-Compiler