Il dispatching è un meccanismo di scelta di una funzione (metodo) specifica da chiamare. In Rust ci sono due approcci: dispatching statico e dinamico.
Storia della questione:
Nei linguaggi OOP, per la chiamata dinamica di un metodo, si utilizza di solito la vtable (tabella virtuale). In Rust questo è realizzato in modo analogo per il trait object — riferimenti a oggetti di tipi che implementano determinati trait. Il dispatching statico si verifica quando si utilizzano generici e vincoli di trait.
Problema:
Spesso è necessario scegliere tra flessibilità (con la possibilità di lavorare con oggetti di diversi tipi attraverso un'interfaccia comune) e prestazioni (il dispatching statico consente di inlineare i metodi). Una scelta errata porta a generici eccessivamente complessi o perdite di prestazioni.
Soluzione:
Il dispatching statico si ottiene tramite parametri generici: in questo caso, il compilatore genera codice separato per ogni tipo. Quello dinamico — se la funzione accetta un argomento di tipo &dyn Trait o Box<dyn Trait>, quindi durante la chiamata del metodo per il trait Rust guarda nella vtable all'indirizzo, come nei classici linguaggi OOP.
Esempio di codice:
trait Shape { fn area(&self) -> f64; } impl Shape for Circle { fn area(&self) -> f64 { 3.1415 * self.radius * self.radius } } fn print_area(shape: &dyn Shape) { // dynamic dispatch println!("area = {}", shape.area()); } // O staticamente: fn print_area_static<S: Shape>(shape: &S) { println!("area = {}", shape.area()); }
Caratteristiche chiave:
dyn Trait utilizza vtable (dispatching dinamico)È possibile fare Box<dyn Sized>?
No. dyn Trait per definizione è unsized, richiede sempre l'uso di Box, Arc o riferimenti, ma non «Box<dyn Sized>» — non ha senso. I trait objects non possiedono il trait Sized.
È consentito dyn Trait per i trait con metodi generici?
No. Non è possibile creare trait object-safe con metodi generici (viene spesso confuso!), i tipi compositi non sono object-safe:
trait MyTrait { fn foo<T>(&self, x: T); } let x: &dyn MyTrait = ... // Errore di compilazione!
È possibile creare dyn Trait per un trait con valori Self nella firma?
No, se il metodo restituisce Self (molti non comprendono questo aspetto: la sicurezza dell'oggetto richiede che nella firma non ci sia Self; si può avere self solo negli argomenti, ma non nel valore restituito).
Si è utilizzato ovunque dyn Trait per la versatilità delle interfacce, anche all'interno di cicli stretti, dove si sarebbe potuto fare a meno dei generici.
Vantaggi:
Svantaggi:
Il dispatching statico è stato utilizzato nella logica interna, mentre dyn Trait — solo ai confini dei moduli.
Vantaggi:
Svantaggi: