为什么有些特质不能做成物体

Why can't some traits be made into 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

什么是特征对象?是

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

这个定义足以解释为什么 ResourceInstance 有效而 ResourceResourceStatic 无效。

ResourceInstance

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

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

ResourceStatic

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

这个特征不能做成一个对象,因为 static_id 可以被调用 没有值 ,这意味着为了调用 static_id 你必须知道具体类型

对于每个特征对象类型(例如dyn ResourceStatic),编译器会自动生成相应特征(ResourceStatic)的实现。这个自动实现使用 vtable 指针作为特征方法中 self 类型的一部分传递。当没有 self 类型时,就没有 vtable 指针,编译器无法自动实现该方法。 Rust 中没有“裸虚表指针”。

为了更好地理解这一点,假设 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 参数来调度。