Dispatch dinamico
Fa parte della sezione Programmazione orientata agli oggetti del percorso Rust di Coddy. Lezione 45 di 61.
Quando usi i generics con vincoli sui trait come <T: Summary>, Rust determina il tipo esatto in fase di compilazione. Questo si chiama dispatch statico: il compilatore genera codice specializzato per ogni tipo concreto che usi. È veloce, ma c’è una limitazione: una variabile può contenere un solo tipo specifico.
E se ti servisse una singola variabile che possa contenere tipi diversi durante l’esecuzione? È qui che entrano in gioco gli oggetti trait. Usando Box<dyn Trait>, puoi memorizzare qualsiasi tipo che implementi il trait:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String { String::from("Woof!") }
}
impl Speak for Cat {
fn speak(&self) -> String { String::from("Meow!") }
}
fn main() {
let animal: Box<dyn Speak> = Box::new(Dog);
println!("{}", animal.speak()); // "Woof!"
let animal: Box<dyn Speak> = Box::new(Cat);
println!("{}", animal.speak()); // "Meow!"
}
La parola chiave dyn indica il dispatch dinamico: Rust determina quale metodo chiamare in fase di esecuzione anziché in fase di compilazione. Box è necessario perché gli oggetti trait non hanno una dimensione nota; Box fornisce un puntatore con dimensione fissa.
Pensala così: i generics dicono «Lavoro con il tipo T», mentre gli oggetti trait dicono «Lavoro con qualsiasi cosa in grado di farlo». Il compromesso è un piccolo costo a runtime in cambio della flessibilità di gestire tipi diversi attraverso la stessa variabile.
Sfida
FacileCreiamo un sistema di noleggio veicoli che dimostri la potenza del dispatch dinamico! Creerai un trait che definisce cosa può fare qualsiasi veicolo noleggiabile, poi userai Box<dyn Trait> per memorizzare diversi tipi di veicoli nella stessa variabile.
Organizzerai il codice in due file:
vehicle.rs: Definisci un trait pubblicoRentablecon un metodorental_infoche accetta&selfe restituisce unaString. Poi crea due struct pubbliche:Car: con un campo pubblicomodel(String). Il suorental_infodovrebbe restituireCar: {model}Bike: con un campo pubblicobrand(String). Il suorental_infodovrebbe restituireBike: {brand}
main.rs: Importa il modulo vehicle e usa gli input per dimostrare il dispatch dinamico. Crea una variabileBox<dyn Rentable>che inizialmente contenga unaCar, stampa le sue informazioni di noleggio, poi riassegna la stessa variabile in modo che contenga unaBikee stampa di nuovo le sue informazioni di noleggio.
La particolarità è che una singola variabile di tipo Box<dyn Rentable> può contenere una Car o una Bike: Rust determina quale metodo rental_info chiamare in fase di esecuzione. Questo è il dispatch dinamico in azione!
L'output dovrebbe mostrare le informazioni di entrambi i veicoli:
Car: {model}
Bike: {brand}Per esempio, con gli input Tesla Model 3 e Trek:
Car: Tesla Model 3
Bike: TrekRiceverai due input: il modello dell'auto e la marca della bicicletta.
Provalo tu
mod vehicle;
use vehicle::{Rentable, Car, Bike};
fn main() {
// Leggi l'input
let mut car_model = String::new();
std::io::stdin().read_line(&mut car_model).expect("Failed to read line");
let car_model = car_model.trim().to_string();
let mut bike_brand = String::new();
std::io::stdin().read_line(&mut bike_brand).expect("Failed to read line");
let bike_brand = bike_brand.trim().to_string();
// TODO: Crea una variabile Box<dyn Rentable> che contenga una Car
// Stampa le informazioni sul noleggio usando il metodo rental_info()
// TODO: Riassegna la stessa variabile affinché contenga una Bike
// Stampa di nuovo le informazioni sul noleggio
}
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