Menu
Coddy logo textTech

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.

challenge icon

Sfida

Facile

Creiamo 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 pubblico Rentable con un metodo rental_info che accetta &self e restituisce una String. Poi crea due struct pubbliche:
    • Car: con un campo pubblico model (String). Il suo rental_info dovrebbe restituire Car: {model}
    • Bike: con un campo pubblico brand (String). Il suo rental_info dovrebbe restituire Bike: {brand}
  • main.rs: Importa il modulo vehicle e usa gli input per dimostrare il dispatch dinamico. Crea una variabile Box<dyn Rentable> che inizialmente contenga una Car, stampa le sue informazioni di noleggio, poi riassegna la stessa variabile in modo che contenga una Bike e 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: Trek

Riceverai 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
    
}
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore Rust online