为什么有些特质不能做成物体
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{});
谁能解释一下幕后发生的事情,这样才不会显得随意?
什么是特征对象?是
- 一个值,
- 具体类型编译器未知,
- 尽管如此实现了一个特征。
这个定义足以解释为什么 ResourceInstance
有效而 Resource
和 ResourceStatic
无效。
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
参数来调度。
我理解 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{});
谁能解释一下幕后发生的事情,这样才不会显得随意?
什么是特征对象?是
- 一个值,
- 具体类型编译器未知,
- 尽管如此实现了一个特征。
这个定义足以解释为什么 ResourceInstance
有效而 Resource
和 ResourceStatic
无效。
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
参数来调度。