Rust te permite abstraer tipos con rasgos o traits. Son similares a las interfaces de java o c#:
struct Dog { name: String, age: i8 }
struct Cat { lives: i8 } // No name needed, cats won't respond anyway.
trait Pet {
fn talk(&self) -> String;
}
impl Pet for Dog {
fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) }
}
impl Pet for Cat {
fn talk(&self) -> String { String::from("Miau!") }
}
fn greet<P: Pet>(pet: &P) {
println!("Oh you're a cutie! What's your name? {}", pet.talk());
}
fn main() {
let captain_floof = Cat { lives: 9 };
let fido = Dog { name: String::from("Fido"), age: 5 };
greet(&captain_floof);
greet(&fido);
}
Los objetos de rasgo permiten valores de diferentes tipos, por ejemplo en una colección:
fn main() {
let pets: Vec<Box<dyn Pet>> = vec![
Box::new(Cat { lives: 9 }),
Box::new(Dog { name: String::from("Fido"), age: 5 }),
];
for pet in pets {
println!("Hello, who are you? {}", pet.talk());
}
}
Disposición de la memoria después de asignar mascotas:
Los tipos que implementan un Trait determinado pueden ser de diferentes tamaños. Esto hace que sea imposible tener cosas como Vec<dyn Pet> en el ejemplo anterior.
dyn Pet es una forma de informarle al compilador acerca de un tipo de tamaño dinámico que implementa Pet.
En el ejemplo, las mascotas se asignan en la pila y los datos vectoriales están en el montón. Los dos elementos del vector son fat pointers o punteros gordos:
Un fat pointers es un puntero de doble ancho. Tiene dos componentes: un puntero al objeto real y un puntero a la tabla de métodos virtuales (vtable) para la implementación Pet de ese objeto en particular.
Los datos del perro llamado Fido son los campos de nombre y edad. El Gato tiene un campo de vida.