I trait in Rust sono un modo per definire un'interfaccia: descrivono un insieme di metodi che un tipo deve implementare. L'implementazione di un trait consente di utilizzare generici e il dispatch dinamico dei metodi.
Dispatch statico (tramite generics e bound):
Viene chiamata un'implementazione specifica del metodo durante la compilazione. Si utilizza codice generico (impl<T: Trait> o fn foo<T: Trait>(t: T)), durante la compilazione per ciascun tipo viene creata una versione separata della funzione (monomorfizzazione).
Dispatch dinamico:
Viene utilizzato quando una funzione accetta oggetti come riferimenti a trait (&dyn Trait), il compilatore non sa in anticipo quale tipo verrà utilizzato. La chiamata al metodo avviene tramite vtable (tabella virtuale) durante l'esecuzione.
Esempio:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } // Dispatch statico def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dispatch dinamico def print_animal_dyn(a: &dyn Animal) { a.speak(); }
Quando usare:
È possibile memorizzare valori con tipo generico trait in Vec<Trait>? Perché?
Risposta:
No, non è possibile. Vec richiede tipi sized, mentre i trait di per sé non hanno dimensione. È necessario utilizzare Box<dyn Trait> o riferimenti ai trait.
// let v: Vec<Trait> = vec![]; // errore di compilazione let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // corretto
Storia
Tentativo di creare una collezione di oggetti trait senza utilizzare Box o Rc:
// let coll: Vec<MyTrait> = vec![]; // errore: la dimensione per valori di tipo ... non può essere conosciuta
Storia
Storia
Nel demo del cliente si stava tentando di memorizzare riferimenti a oggetti temporanei (&dyn Trait) per interagire con diversi protocolli, portando a riferimenti pendenti e panic a runtime. È stato necessario passare a Box<dyn Trait> con proprietà esplicita.