是否允许多态变量?

时间:2015-01-29 16:19:11

标签: rust

我有各种结构都实现了相同的特性。我想在某些条件下进行分支,在运行时决定实例化哪些结构。然后,无论我遵循哪个分支,我都想从该特征中调用方法。

这可能在Rust吗?我希望实现类似下面的内容(不编译):

trait Barks {
    fn bark(&self);
}

struct Dog;

impl Barks for Dog {
    fn bark(&self) {
        println!("Yip.");
    }
}

struct Wolf;

impl Barks for Wolf {
    fn bark(&self) {
        println!("WOOF!");
    }
}

fn main() {
    let animal: Barks;
    if 1 == 2 {
        animal = Dog;
    } else {
        animal = Wolf;
    }
    animal.bark();
}

3 个答案:

答案 0 :(得分:16)

是的,但不是那么容易。你在那里写的是animal应该是Barks类型的变量,但Barks是一个特征;接口的描述。特征没有静态定义的大小,因为任何大小的类型都可以出现impl Barks。编译器不知道有多大animal

您需要做的是添加一个间接层。在这种情况下,您可以使用Box,但您也可以使用Rc或简单引用等内容:

fn main() {
    let animal: Box<Barks>;
    if 1 == 2 {
        animal = Box::new(Dog);
    } else {
        animal = Box::new(Wolf);
    }
    animal.bark();
}

在这里,我在堆上分配DogWolf,然后将其转换为Box<Barks>。这就像类似就像在C#或Java之类的东西中将对象转换为接口,或者在C ++中将Dog*转换为Barks*

您可以使用的完全不同的方法是枚举。您可以enum Animal { Dog, Wolf }然后定义impl Animal { fn bark(&self) { ... } }。取决于您是否需要一套完全开放的动物和/或多种特征。

最后,请注意上面的“种类”。有些东西不像Java / C#/ C ++那样有效。例如,Rust没有向下转换(你不能从Box<Barks>回到Box<Dog>,或从一个特征转到另一个特征。此外,这仅适用于特征是“对象安全”(无泛型,不使用selfSelf按值)。

答案 1 :(得分:16)

DK有一个很好的解释,我只是在一个例子中,我们在堆栈上分配DogWolf,避免堆分配:

fn main() {
    let dog;
    let wolf;
    let animal: &Barks;
    if 1 == 2 {
        dog = Dog;
        animal = &dog;
    } else {
        wolf = Wolf;
        animal = &wolf;
    }
    animal.bark();
}

它有点难看,但引用完成了与Box相同的间接,而且开销较小。

答案 2 :(得分:4)

定义自定义枚举是执行此操作的最有效方法。这将允许您在堆栈上精确分配所需的空间量,即最大选项的大小,再加上1个额外字节来跟踪存储的选项。与使用Box或特征引用的解决方案不同,它还允许在没有间接级别的情况下直接访问。

不幸的是,它需要更多的锅炉板:

enum WolfOrDog {
    IsDog(Dog),
    IsWolf(Wolf)
}
use WolfOrDog::*;

impl Barks for WolfOrDog {
    fn bark(&self) {
        match *self {
            IsDog(ref d) => d.bark(),
            IsWolf(ref w) => w.bark()
        }
    }
}

fn main() {
    let animal: WolfOrDog;
    if 1 == 2 {
        animal = IsDog(Dog);
    } else {
        animal = IsWolf(Wolf);
    }
    animal.bark();
}

main中,我们只使用一个堆栈分配变量,并保存自定义枚举的实例。