为什么不能将某些特征变成对象

时间:2020-10-17 23:05:43

标签: rust trait-objects

我了解the rules何时可以将特征转化为特征对象,但是我不理解为什么存在这些规则。

例如:

trait Resource {
    const RESOURCE_ID: u64;
}

trait ResourceStatic {
    fn static_id() -> u64;
}

trait ResourceInstance {
    fn resource_id(&self) -> u64;
}

struct MyResource {}

impl Resource for MyResource {
    const RESOURCE_ID: u64 = 123;
}

impl ResourceStatic for MyResource {
    fn static_id() -> u64 {
        123
    }
}

impl ResourceInstance for MyResource {
    fn resource_id(&self) -> u64 {
        123
    }
}

在我看来,这三个特征基本上都封装了相同的功能。那么为什么不允许这些:

let _: Box<dyn Resource> = Box::new(MyResource{});
let _: Box<dyn ResourceStatic> = Box::new(MyResource{});

但这是吗?

let _: Box<dyn ResourceInstance> = Box::new(MyResource{});

有人可以解释一下幕后发生的事情吗,以免看起来有些武断?

Rust playground

1 个答案:

答案 0 :(得分:4)

什么是特征对象?是

  • 具体类型编译器未知
  • 尽管如此,但实现了特征

此定义足以说明ResourceInstanceResource不起作用的原因,ResourceStatic起作用。

ResourceInstance

trait ResourceInstance {
    fn resource_id(&self) -> u64;
}

可以将这个特征变成一个对象,因为即使具体类型未知,您仍然可以在实现该特征的值上调用resource_id (通过将其作为self参数传递)。

ResourceStatic

trait ResourceStatic {
    fn static_id() -> u64;
}

此特征不能成为对象,因为static_id可以被称为而没有值,这意味着要调用static_id,您必须知道具体类型

对于每种特征对象类型(例如dyn ResourceStatic),编译器会自动生成相应特征(ResourceStatic)的实现。此自动实现使用在trait方法中作为self类型的一部分传递的vtable指针。如果没有self类型,则没有vtable指针,并且编译器无法自动实现该方法。 Rust中没有“裸vtable指针”。

为更好地理解这一点,请设想dyn ResourceStatic是有效类型。 <dyn ResourceStatic>::static_id()是做什么的?它不能遵循具体类型的实现,因为它没有价值,因此也没有具体类型。我们是否可以得出结论,dyn ResourceStatic 没有实现 ResourceStatic?这显然是错误的。还是dyn ResourceStatic有自己的ResourceStatic实现,没有遵循某种具体类型?这也是没有道理的,因为dyn ResourceStatic的重点是要代表一种具体的类型。

Rust解决此问题的方法只是拒绝将dyn ResourceStatic作为类型。

Resource

trait Resource {
    const RESOURCE_ID: u64;
}

由于ResourceStatic不能出于相同的原因,无法将此特征制成对象:因为特征对象类型dyn Resource不可能自动满足特征要求。

TL; DR

如果要在类型Self上进行动态分派,则需要一个self参数进行分派。