请考虑以下代码:
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());
}
}
当我尝试推送v
(类型不匹配)时,编译器告诉我Animal
是cat
的向量
那么,我如何制作属于特征的对象向量并在每个元素上调用相应的特征方法呢?
答案 0 :(得分:60)
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
是一个特征,而且特征没有大小(Cat
和Dog
不能保证大小相同。)
要解决此问题,我们需要存储大小在Vec
的内容。最直接的解决方案是将值包装在Box
中,即Vec<Box<Animal>>
。 Box<T>
具有固定的大小(&#34;胖指针&#34;如果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());
}
}
答案 1 :(得分:13)
您可以使用参考特征对象&Animal
借用元素并将这些特征对象存储在Vec
中。然后,您可以枚举它并使用特征的界面。
通过在特征前添加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());
}
如果您希望原始变量保留所有权并在以后重复使用,那么这很好。
请注意上面的方案,您无法转移dog
或cat
的所有权,因为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());
}