我有各种结构,都实现相同的特征.我想在某些条件下进行分支,在运行时决定实例化哪些结构.然后,无论我遵循哪个分支,我都想从该特征中调用方法.
这可能在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)
在这里,我正在分配Dog或Wolf在堆上,然后将其转换为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>,或从一个特征到另一个特征).此外,这仅在特征是"对象安全"(没有泛型,没有使用self或Self按值)时才有效.
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开销较小.
定义自定义枚举是执行此操作的最有效方法。这将允许您在堆栈上准确分配所需的空间量,即最大选项的大小,加上 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我们只使用一个堆栈分配的变量,保存我们自定义枚举的一个实例。