属于特征的对象传染媒介

时间:2014-09-12 23:02:58

标签: polymorphism rust

请考虑以下代码:

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(类型不匹配)时,编译器告诉我Animalcat的向量

那么,我如何制作属于特征的对象向量并在每个元素上调用相应的特征方法呢?

2 个答案:

答案 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是一个特征,而且特征没有大小(CatDog不能保证大小相同。)

要解决此问题,我们需要存储大小在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());
}

如果您希望原始变量保留所有权并在以后重复使用,那么这很好。

请注意上面的方案,您无法转移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());
}