为什么有些特质不能做成对象

wro*_*ame 7 rust trait-objects

我理解的规则,当一个特征可以做成一个特征对象,但我不明白为什么这些规则的存在。

例如:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

let _: Box<dyn Resource> = Box::new(MyResource{});
let _: Box<dyn ResourceStatic> = Box::new(MyResource{});
Run Code Online (Sandbox Code Playgroud)

但这是?

let _: Box<dyn ResourceInstance> = Box::new(MyResource{});
Run Code Online (Sandbox Code Playgroud)

有人可以解释一下幕后发生的事情,使其看起来不随意吗?

铁锈游乐场

tre*_*tcl 8

什么是特质对象?这是

  • 一个
  • 编译器未知的具体类型,
  • 尽管如此,它还是实现了一个特质

这个定义足以解释为什么ResourceInstancewhile 有效ResourceResourceStatic不起作用。

ResourceInstance

trait ResourceInstance {
    fn resource_id(&self) -> u64;
}
Run Code Online (Sandbox Code Playgroud)

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

ResourceStatic

trait ResourceStatic {
    fn static_id() -> u64;
}
Run Code Online (Sandbox Code Playgroud)

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

对于每个特征对象类型(例如dyn ResourceStatic),编译器自动生成相应特征()的实现ResourceStatic。此自动实现使用 vtable 指针作为self特征方法中类型的一部分传递。当没有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;
}
Run Code Online (Sandbox Code Playgroud)

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

长话短说

如果您想在类型上进行动态调度Self,则需要一个self参数来进行调度。