7

我了解何时可以将特征制成特征对象的规则,但我不明白为什么存在这些规则。

例如:

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

有人可以解释一下引擎盖下发生了什么,以免它看起来很随意吗?

锈操场

4

1 回答 1

4

什么是特征对象?这是

  • 一个
  • 编译器不知道的具体类型,
  • 它仍然实现了一个 trait

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

ResourceInstance

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

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

ResourceStatic

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

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

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

为了更好地理解这一点,imaginedyn ResourceStatic是一个有效的类型。做什么<dyn ResourceStatic>::static_id()?它不能遵循具体类型的实现,因为没有值,因此没有具体类型。我们应该得出结论,dyn ResourceStatic 不实施 ResourceStatic吗?这显然是错误的。还是dyn ResourceStatic有自己的实现ResourceStatic遵循某些具体类型?这也没有任何意义,因为它的全部意义在于dyn ResourceStatic代表一个具体的类型。

Rust 解决这个问题的方式就是简单地拒绝dyn ResourceStatic作为一种类型。

Resource

trait Resource {
    const RESOURCE_ID: u64;
}

这个 trait 不能被做成一个对象,原因和ResourceStatic不能一样:因为 trait 对象类型不可能dyn Resource自动满足 trait 的要求。

TL;博士

如果你想对 type 进行动态调度Self,你需要一个self参数来调度。

于 2020-10-18T02:08:31.117 回答