Vector de objetos que pertenecen a un rasgo

Considera el siguiente código:

trait Animal { fn make_sound(&self) -> String; } struct Cat; impl Animal for Cat { fn make_sound(&self) -> String { "meow".to_string() } } struct Dog; impl Animal for Dog { fn make_sound(&self) -> String { "woof".to_string() } } fn main () { let dog: Dog = Dog; let cat: Cat = Cat; let v: Vec = Vec::new(); v.push(cat); v.push(dog); for animal in v.iter() { println!("{}", animal.make_sound()); } } 

El comstackdor me dice que v es un vector de Animal cuando bash empujar a cat (no coincide con el tipo)

Entonces, ¿cómo puedo hacer un vector de objetos que pertenecen a un rasgo y llamar al método de rasgo correspondiente en cada elemento?

Vec no es legal, pero el comstackdor no puede decirte eso porque el tipo no coincide de alguna manera lo oculta. Si eliminamos las llamadas para push , el comstackdor nos da el siguiente error:

 :22:9: 22:40 error: instantiating a type parameter with an incompatible type `Animal`, which does not fulfill `Sized` [E0144] :22 let mut v: Vec = Vec::new(); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

La razón por la que eso no es legal es que un Vec almacena muchos objetos T consecutivamente en la memoria. Sin embargo, Animal es un rasgo, y los rasgos no tienen tamaño (no se garantiza que un Cat y un Dog tengan el mismo tamaño).

Para resolver este problema, debemos almacenar algo que tenga un tamaño en el Vec . La solución más directa es envolver los valores en un Box , es decir, Vec> . Box tiene un tamaño fijo (un “puntero gordo” si T es un rasgo, un puntero simple en caso contrario).

Aquí hay un trabajo main :

 fn main () { let dog: Dog = Dog; let cat: Cat = Cat; let mut v: Vec> = Vec::new(); v.push(Box::new(cat)); v.push(Box::new(dog)); for animal in v.iter() { println!("{}", animal.make_sound()); } } 

Puede usar un objeto de rasgo de referencia &Animal para tomar prestados los elementos y almacenar estos objetos de rasgo en un Vec . Luego puede enumerarlo y usar la interfaz del rasgo.

Alterar el tipo genérico de Vec añadiendo un & delante del rasgo funcionará:

 fn main() { let dog: Dog = Dog; let cat: Cat = Cat; let mut v: Vec<&Animal> = Vec::new(); // ~~~~~~~ v.push(&dog); v.push(&cat); for animal in v.iter() { println!("{}", animal.make_sound()); } // Ownership is still bound to the original variable. println!("{}", cat.make_sound()); } 

Esto es genial si desea que la variable original mantenga la propiedad y la reutilice más adelante.

Tenga en cuenta que con el escenario anterior, no puede transferir la propiedad de un dog o un cat porque el Vec ha tomado prestadas estas instancias concretas en el mismo ámbito.

La introducción de un nuevo scope puede ayudar a manejar esa situación particular:

 fn main() { let dog: Dog = Dog; let cat: Cat = Cat; { let mut v: Vec<&Animal> = Vec::new(); v.push(&dog); v.push(&cat); for animal in v.iter() { println!("{}", animal.make_sound()); } } let pete_dog: Dog = dog; println!("{}", pete_dog.make_sound()); }