为什么不能将具有扩展特征的Box投射为具有基本特征的Box? [英] Why can't I cast a Box with an extended trait to a Box with the base trait?

查看:44
本文介绍了为什么不能将具有扩展特征的Box投射为具有基本特征的Box?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出代码

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;
}

当我确定要编译时,我会收到以下错误消息:

When I compile as I'm sure you know I get the following error message:

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.有什么办法吗?如果不是这样,例如,存储具有相同基本特征的不同特征向量的常用方法是什么?

Why am I not allowed to do this? If a Box contains a Dervied it is guaranteed that this also contains a Base. Is there any way to do this? And if not, what is a common way to for instance store a vector of different Traits that all have the same base Trait?

推荐答案

简短的答案是因为特征不是接口

长答案是因为& Base 特征对象和& Derived 特征对象不是同一件事. vtables 是不同的,因为 Derived Base 是不同的特征. Derived 的vtable将包括所有 Dervied 的方法以及所有 Base 的方法,而&Base 仅包含 Base 的方法.

The long answer is because a &Base trait object and a &Derived trait object are not the same thing. The vtables are different because Derived and Base are different traits. The vtable for Derived would include all of Dervied's methods as well as all of Base's while the vtable for &Base would only include Base's methods.

很明显,现在 Base 的方法 & 的vtable中.因此,也许您可​​以做一些聪明的事情,并得到想要的行为:

Now, obviously, Base's methods are in &Derived's vtable. So perhaps you could do something clever and get the behavior you want:

  1. 如果在& Derived 的vtable中首先列出了 Base 的方法,那么您只需转换& Derived & Base ,这将起作用.但是,& Derived & Base vtable的长度不同,这样做会截断& Base 末尾的所有内容.因此,如果您尝试在该对象上调用属于 Derived 的一部分的方法,则

  1. If the Base's methods were listed first in &Derived's vtable, then you could just cast &Derived to &Base and that would work. However, &Derived and &Base vtables have different lengths and doing so would chop off everything past the end of &Base. So if you try to call a method on that object which is part of Derived, you'll invoke undefined behavior.

您可以运行一些魔术代码来分析& Base & Derived 的定义,并能够为构造一个vtable& Base 来自& Derived .这将需要在运行时有关这些类型及其布局的其他信息.除了额外的内存使用量之外,这也将具有非零的性能成本.Rust的基本原则之一是零成本抽象",这通常意味着潜在的昂贵操作是显式的,而不是隐式的(如果 let a:Box< Base> = b; 这样做,通常被认为太隐含).

You could run some magic code which would analyze the definitions of &Base and &Derived and be able to construct a vtable for &Base from &Derived. This would require additional information at runtime about these types and their layout. This would also have a non-zero performance cost in addition to the additional memory usage. One of the basic principles of Rust is "zero cost abstractions" which generally means that potentially expensive operations are explicit and not implicit (if let a: Box<Base> = b; did this, it would generally be considered too implicit).


通常很难说什么是更好的模式.如果您要建模一组封闭的项目,通常使用枚举是一种更好的方法:


It's difficult to say in general what a better pattern is. If you are modeling a closed-set of items, enums are generally a better way to go:

enum Animal {
  Dog { name: String, age: u8 },
  Cat { name: String, age: u8, sleeping: bool },
  Fish { name: String, age: u8, in_ocean: bool },
}

如果您尝试做更复杂的事情,请实体组件系统,例如 规范 比简单的枚举可以给您更大的灵活性.

If you are trying to do something more complicated, Entity Component Systems like specs can give you a lot more flexibility than a simple enum.

这篇关于为什么不能将具有扩展特征的Box投射为具有基本特征的Box?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆