Menu
Coddy logo textTech

Iterowanie po obiektach cech

Część sekcji Programowanie obiektowe ścieżki Rust w Coddy. Lekcja 47 z 61.

Skoro potrafisz już przechowywać różne typy w Vec<Box<dyn Trait>>, kolejnym krokiem jest przetwarzanie tej kolekcji. Iterowanie po obiektach cech działa tak samo jak iterowanie po dowolnej tablicy. Przechodzisz przez każdy element i wywołujesz metody zdefiniowane przez cechę.

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());
    }
}

Gdy iterujesz za pomocą for shape in &shapes, każdy shape jest referencją do Box<dyn Shape>. Możesz bezpośrednio wywołać dowolną metodę z cechy Shape: Rust automatycznie obsługuje dynamiczne wywoływanie metod, wywołując właściwą implementację dla każdego konkretnego typu.

Ten wzorzec jest przydatny do agregowania wyników. Możesz zsumować wszystkie pola, filtrować kształty według rozmiaru lub przekształcić kolekcję. Kluczowy wniosek jest taki, że gdy elementy znajdą się w kolekcji obiektów cechy, korzystasz z nich wyłącznie za pośrednictwem interfejsu tej cechy, niezależnie od ich typów bazowych.

challenge icon

Wyzwanie

Łatwy

Zbudujmy kalkulator cen do koszyka zakupowego, który może zawierać różne rodzaje produktów! Stworzysz system, w którym produkty i usługi, z których każdy ma inną logikę ustalania ceny, można przechowywać razem i przetwarzać w celu obliczenia łącznej ceny.

Rozmieścisz swój kod w dwóch plikach:

  • items.rs: Zdefiniuj publiczny trait Priceable z metodą price, która przyjmuje &self i zwraca f64. Następnie utwórz dwie publiczne struktury:
    • Product: z publicznymi polami name (String) i cost (f64). Metoda price powinna po prostu zwracać wartość cost.
    • Service: z publicznymi polami name (String), hourly_rate (f64) i hours (f64). Metoda price powinna zwracać wartość hourly_rate * hours.
  • main.rs: Dołącz moduł items i utwórz wektor typu Vec<Box<dyn Priceable>>. Użyj danych wejściowych, aby utworzyć jeden produkt i jedną usługę, dodaj je do wektora, a następnie przeiteruj po kolekcji, aby obliczyć i wyświetlić łączną cenę wszystkich elementów.

Właśnie tutaj widać zalety obiektów traitów. Twój kod iterujący nie musi wiedzieć, czy przetwarza produkt, czy usługę. Po prostu wywołuje price() dla każdego elementu, a Rust automatycznie wybiera właściwą implementację.

Wynik powinien wyświetlać łączną cenę z dwoma miejscami po przecinku:

Total: ${total}

Na przykład dla danych wejściowych Laptop, 999.99, Consulting, 150.0 i 3.0:

Total: $1449.99

Oraz dla danych wejściowych Book, 29.99, Tutoring, 50.0 i 2.0:

Total: $129.99

Otrzymasz pięć danych wejściowych: nazwę produktu, cenę produktu, nazwę usługi, stawkę godzinową i liczbę przepracowanych godzin (wartości liczbowe sparsuj jako f64).

Spróbuj swoich sił

mod items;

use items::{Priceable, Product, Service};

fn main() {
    // Wczytaj dane wejściowe
    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: Utwórz Product, używając product_name i product_cost

    // TODO: Utwórz Service, używając service_name, hourly_rate i hours

    // TODO: Utwórz Vec<Box<dyn Priceable>> i dodaj do niego oba elementy

    // TODO: Przejdź przez wektor i oblicz łączną cenę

    // TODO: Wyświetl sumę z jednym miejscem po przecinku
    // Format: Total: ${total:.1}
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Rust online