是否允许多态变量?

rlk*_*024 14 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();
}
Run Code Online (Sandbox Code Playgroud)

DK.*_*DK. 17

是的,但不是那么容易.你在那里写的是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();
}
Run Code Online (Sandbox Code Playgroud)

在这里,我正在分配DogWolf在堆上,然后将其转换为Box<Barks>.这有点像将对象转换为类似C#或Java的接口,或者在C++中转换Dog*为a Barks*.

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

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


She*_*ter 16

DK有一个很好的解释,我将简单介绍一下我们在堆栈上分配Dog或者Wolf堆栈的示例,避免堆分配:

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

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


win*_*ner 5

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

不幸的是,它确实需要更多样板:

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();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为`Canine` 是枚举的更好名称。 (3认同)