Iterare sugli oggetti trait
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 47 di 61.
Ora che puoi memorizzare tipi diversi in un Vec<Box<dyn Trait>>, il passo successivo è elaborare quella raccolta. Iterare sugli oggetti trait funziona proprio come iterare su qualsiasi vettore. Scorri ogni elemento e chiami i metodi definiti dal trait.
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());
}
}
Quando iteri con for shape in &shapes, ogni shape è un riferimento a un Box<dyn Shape>. Puoi chiamare direttamente qualsiasi metodo del trait Shape: Rust gestisce automaticamente il dispatch dinamico, chiamando l'implementazione corretta per ciascun tipo concreto.
Questo schema è potente per aggregare i risultati. Potresti sommare tutte le aree, filtrare le forme in base alle dimensioni o trasformare la raccolta. L'intuizione chiave è che, una volta inseriti gli elementi in una raccolta di oggetti trait, interagisci con essi esclusivamente tramite l'interfaccia del trait, indipendentemente dai loro tipi sottostanti.
Sfida
FacileCreiamo un calcolatore dei prezzi per un carrello della spesa che può contenere diversi tipi di articoli! Creerai un sistema in cui prodotti e servizi, ciascuno con una logica di prezzo diversa, possono essere archiviati insieme ed elaborati per calcolare il prezzo totale.
Organizzerai il codice in due file:
items.rs: Definisci un trait pubblicoPriceablecon un metodopriceche accetta&selfe restituisce unf64. Poi crea due struct pubbliche:Product: con i campi pubbliciname(String) ecost(f64). Il suo metodopricedeve semplicemente restituirecost.Service: con i campi pubbliciname(String),hourly_rate(f64) ehours(f64). Il suo metodopricedeve restituirehourly_rate * hours.
main.rs: Importa il modulo items e crea un vettore di tipoVec<Box<dyn Priceable>>. Usa gli input per creare un Product e un Service, aggiungili al vettore, poi scorri la raccolta per calcolare e stampare il prezzo totale di tutti gli articoli.
È qui che gli oggetti trait mostrano tutta la loro potenza. Il codice del ciclo non deve sapere se sta elaborando un Product o un Service. Chiama semplicemente price() su ogni elemento e Rust seleziona automaticamente l'implementazione corretta.
L'output deve mostrare il prezzo totale con due cifre decimali:
Total: ${total}Ad esempio, con gli input Laptop, 999.99, Consulting, 150.0 e 3.0:
Total: $1449.99E con gli input Book, 29.99, Tutoring, 50.0 e 2.0:
Total: $129.99Riceverai cinque input: il nome del prodotto, il costo del prodotto, il nome del servizio, la tariffa oraria e le ore lavorate (analizza i valori numerici come f64).
Provalo tu
mod items;
use items::{Priceable, Product, Service};
fn main() {
// Leggi i dati in input
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: Crea un Product usando product_name e product_cost
// TODO: Crea un Service usando service_name, hourly_rate e hours
// TODO: Crea un Vec<Box<dyn Priceable>> e aggiungici entrambi gli elementi
// TODO: Scorri il vettore e calcola il prezzo totale
// TODO: Stampa il totale con una cifra decimale
// Formato: Total: ${total:.1}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Metodi e comportamento
Introduzione ai blocchi implIl parametro selfMetodi mutabiliFunzioni associateBlocchi impl multipliConcatenamento di metodiRiepilogo - Azioni sul rettangolo4Progetto: animale domestico virtuale
Definire l'animaleNutrire l'animale7Trait standard
Il trait DebugIl trait DisplayClone e CopyTrait di uguaglianzaRiepilogo - Punto stampabile10Progetto: sistema di documenti
Il trait DrawComponente di testo2Incapsulamento e moduli
Nozioni di base sui moduliLa parola chiave publicCampi privatiGetterSetterRiepilogo - Armadietto sicuro5Generics
Struct genericheMetodi genericiPiù tipi genericiFunzioni genericheRipasso - Punto di coordinate8I Trait come vincoli
Sintassi dei vincoli sui traitVincoli multipliLa clausola whereRestituire tipi con i traitRiepilogo - Stampante generica11Modelli di progettazione in Rust
Pattern NewtypeComposizioneIl trait DropFrom e IntoRipasso - Mock con smart pointer3Enumerazioni avanzate
Enumerazioni con datiMetodi sulle enumerazioniCorrispondenza delle varianti con datiL'enumerazione Option ripresaRiepilogo - enumerazione Shape6Definizione dei trait
Che cos’è un trait?Implementare i traitImplementazioni predefiniteSovrascrivere le implementazioni predefiniteTrait con parametriRiepilogo - Lettore multimediale9Oggetti trait
Dispatch dinamicoVettori di traitIterare sugli oggetti traitRiepilogo - Gestore dello zooEsercitati da solo: Compilatore Rust online