为什么我不能将具有扩展特征的 Box 投射到具有基本特征的 Box?
Why can't I cast a Box with an extended trait to a Box with the base trait?
给定代码
trait Base { }
trait Derived : Base { }
struct Foo { }
impl Base for Foo { }
impl Derived for Foo { }
fn main()
{
let b : Box<Derived> = Box::new( Foo { } );
let a : Box<Base> = b;
}
当我确定您知道我进行编译时,我收到以下错误消息:
error[E0308]: mismatched types
--> src/main.rs:14:25
|
14 | let a : Box<Base> = b;
| ^ expected trait `Base`, found trait `Derived`
|
= note: expected type `std::boxed::Box<Base>`
found type `std::boxed::Box<Derived>`
为什么不允许我这样做?如果一个 Box 包含一个 Dervied,则保证它也包含一个 Base。有什么办法吗?如果不是,例如存储具有相同基本特征的不同特征的向量的常用方法是什么?
简短的回答是因为 .
长答案是因为 &Base
特征对象和 &Derived
特征对象不是一回事。 vtables 是不同的,因为 Derived
和 Base
是不同的特征。 Derived
的 vtable 将包括 Dervied
的所有方法以及 Base
的所有方法,而 &Base
的 vtable 将仅包括 Base
'方法。
现在,显然,Base
的方法 是 在 &Derived
的 vtable 中。所以也许你可以做一些聪明的事情并获得你想要的行为:
如果 Base
的方法在 &Derived
的 vtable 中排在第一位,那么您可以将 &Derived
转换为 &Base
并且那行得通。但是,&Derived
和 &Base
vtables 有不同的长度,这样做会切断 &Base
结束后的所有内容。因此,如果您尝试调用属于 Derived
、you'll invoke undefined behavior 的对象的方法。
你可以 运行 一些神奇的代码来分析 &Base
和 &Derived
的定义,并能够为 &Base
构造一个 vtable来自 &Derived
。这将需要在 运行 时提供有关这些类型及其布局的其他信息。除了额外的内存使用外,这还会产生 non-zero 性能成本。 Rust 的基本原则之一是 "zero cost abstractions",这通常意味着潜在的昂贵操作是显式的而不是隐式的(如果 let a: Box<Base> = b;
这样做,通常会被认为过于隐式)。
很难笼统地说什么是更好的模式。如果您正在为 closed-set 项建模,枚举通常是更好的方法:
enum Animal {
Dog { name: String, age: u8 },
Cat { name: String, age: u8, sleeping: bool },
Fish { name: String, age: u8, in_ocean: bool },
}
如果您想做一些更复杂的事情,Entity Component Systems like specs
可以比简单的枚举给您更多的灵活性。
给定代码
trait Base { }
trait Derived : Base { }
struct Foo { }
impl Base for Foo { }
impl Derived for Foo { }
fn main()
{
let b : Box<Derived> = Box::new( Foo { } );
let a : Box<Base> = b;
}
当我确定您知道我进行编译时,我收到以下错误消息:
error[E0308]: mismatched types
--> src/main.rs:14:25
|
14 | let a : Box<Base> = b;
| ^ expected trait `Base`, found trait `Derived`
|
= note: expected type `std::boxed::Box<Base>`
found type `std::boxed::Box<Derived>`
为什么不允许我这样做?如果一个 Box 包含一个 Dervied,则保证它也包含一个 Base。有什么办法吗?如果不是,例如存储具有相同基本特征的不同特征的向量的常用方法是什么?
简短的回答是因为
长答案是因为 &Base
特征对象和 &Derived
特征对象不是一回事。 vtables 是不同的,因为 Derived
和 Base
是不同的特征。 Derived
的 vtable 将包括 Dervied
的所有方法以及 Base
的所有方法,而 &Base
的 vtable 将仅包括 Base
'方法。
现在,显然,Base
的方法 是 在 &Derived
的 vtable 中。所以也许你可以做一些聪明的事情并获得你想要的行为:
如果
Base
的方法在&Derived
的 vtable 中排在第一位,那么您可以将&Derived
转换为&Base
并且那行得通。但是,&Derived
和&Base
vtables 有不同的长度,这样做会切断&Base
结束后的所有内容。因此,如果您尝试调用属于Derived
、you'll invoke undefined behavior 的对象的方法。你可以 运行 一些神奇的代码来分析
&Base
和&Derived
的定义,并能够为&Base
构造一个 vtable来自&Derived
。这将需要在 运行 时提供有关这些类型及其布局的其他信息。除了额外的内存使用外,这还会产生 non-zero 性能成本。 Rust 的基本原则之一是 "zero cost abstractions",这通常意味着潜在的昂贵操作是显式的而不是隐式的(如果let a: Box<Base> = b;
这样做,通常会被认为过于隐式)。
很难笼统地说什么是更好的模式。如果您正在为 closed-set 项建模,枚举通常是更好的方法:
enum Animal {
Dog { name: String, age: u8 },
Cat { name: String, age: u8, sleeping: bool },
Fish { name: String, age: u8, in_ocean: bool },
}
如果您想做一些更复杂的事情,Entity Component Systems like specs
可以比简单的枚举给您更多的灵活性。