Los traits en Rust son una forma de definir una interfaz: describen un conjunto de métodos que debe ser implementado por un tipo. La implementación de un trait permite utilizar genéricos y el despacho dinámico de métodos.
Dispatch estático (a través de genéricos y límites):
Se llama a la implementación concreta del método en tiempo de compilación. Se utiliza código genérico (impl<T: Trait> o fn foo<T: Trait>(t: T)), y durante la compilación se crea una versión separada de la función para cada tipo (monomorfización).
Dispatch dinámico:
Se utiliza cuando la función toma objetos como referencias a un trait (&dyn Trait), el compilador no sabe de antemano qué tipo se utilizará. La llamada al método se realiza a través de vtable (tabla virtual) en tiempo de ejecución.
Ejemplo:
trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("¡Guau!"); } } // Dispatch estático def print_animal_static<T: Animal>(a: T) { a.speak(); } // Dispatch dinámico def print_animal_dyn(a: &dyn Animal) { a.speak(); }
Cuándo usar:
¿Se puede almacenar valores con tipo genérico trait en Vec<Trait>? ¿Por qué?
Respuesta:
No, no se puede hacer eso. Vec requiere tipos de tamaño conocido, y los traits en sí no tienen tamaño. Se necesita usar Box<dyn Trait> o referencias a traits.
// let v: Vec<Trait> = vec![]; // error de compilación let v: Vec<Box<dyn Animal>> = vec![Box::new(Dog)]; // correcto
Historia
Intento de crear una colección de objetos trait sin usar Box o Rc:
// let coll: Vec<MyTrait> = vec![]; // error: el tamaño para los valores de tipo ... no se puede conocer
Historia
Historia
En una demostración del cliente, se intentó almacenar referencias a objetos temporales (&dyn Trait) para interactuar con diferentes protocolos, lo que llevó a referencias colgantes y pánico en tiempo de ejecución. Fue necesario cambiar a Box<dyn Trait> con propiedad explícita.