给出代码
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。有没有办法做到这一点?如果没有,例如存储具有相同基本特征的不同特征的矢量的常用方法是什么?
答案 0 :(得分:2)
简短回答是因为traits are not interfaces。
答案很长,因为&Base
特征对象和&Derived
特征对象不是一回事。 vtables不同,因为Derived
和Base
是不同的特征。 Derived
的vtable包含Dervied
的所有方法以及Base
的所有方法,而&Base
的vtable只包含Base
'的方法。
现在,显然Base
的方法在<{1}}的vtable中。所以也许你可以做一些聪明的事情并获得你想要的行为:
如果在&Derived
的vtable中首先列出了Base
的方法,那么您可以将&Derived
投射到&Derived
,这样就行了。但是,&Base
和&Derived
vtables有不同的长度,这样做会切断&Base
末尾的所有内容。因此,如果您尝试在该对象上调用方法,该方法属于&Base
,you'll invoke undefined behavior。
您可以运行一些魔术代码来分析Derived
和&Base
的定义,并能够从&Derived
构建&Base
的vtable。这将需要在运行时有关这些类型及其布局的附加信息。除了额外的内存使用量之外,这还将具有非零的性能成本。 Rust的基本原则之一是“零成本抽象”,这通常意味着潜在的昂贵操作是明确的而不是隐含的(如果&Derived
这样做,通常会被认为过于隐含)。
一般来说很难说一个更好的模式是什么。如果您要为一组封闭的项目建模,那么枚举通常是更好的方法:
let a: Box<Base> = b;
如果您尝试执行更复杂的操作,Entity Component Systems specs
可以提供比简单枚举更多的灵活性。