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

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

问题描述

Given the code

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>`

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?

解决方案

The short answer is because traits are not interfaces.

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.

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

  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.

  2. 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.

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

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