属于特征的对象的向量

考虑以下代码:

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<Animal> = Vec::new();
v.push(cat);
v.push(dog);
for animal in v.iter() {
println!("{}", animal.make_sound());
}
}

当我尝试推入 cat(类型不匹配)时,编译器告诉我 vAnimal的向量

那么,我如何创建一个属于 trait 的对象向量并对每个元素调用相应的 trait 方法呢?

33680 次浏览

Vec<Animal>是不合法的,但是编译器不能告诉您这一点,因为类型不匹配以某种方式隐藏了它。如果我们删除对 push的调用,编译器会给我们以下错误:

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

之所以不合法是因为 Vec<T>在内存中连续存储许多 T对象。然而,Animal是一个性状,性状没有大小(CatDog不能保证具有相同的大小)。

为了解决这个问题,我们需要在 Vec中存储具有大小的内容。最直接的解决方案是将值包装在 Box中,即 Vec<Box<Animal>>Box<T>有一个固定的大小(如果 T是一个特性,则为“胖指针”,否则为简单指针)。

这是一个工作 main:

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

您可以使用引用 trait 对象 &Animal来借用元素并将这些 trait 对象存储在 Vec中。然后您可以枚举它并使用 trait 的接口。

通过在特征前添加一个 &来改变 Vec的通用类型将会奏效:

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());
}

如果您希望原始变量保持所有权并在以后重用它,那么这非常好。

请记住上面的场景,您不能转移 dogcat的所有权,因为 Vec在相同的范围内借用了这些具体实例。

引入一个新的范围可以帮助处理这种特殊情况:

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());
}