I understand the rules for when a trait can be made into a trait object, but I don't understand why these rules exist.
For example:
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
}
}
It seems to me all three traits are basically encapsulating the same functionality. Why is it then that these are not allowed:
let _: Box<dyn Resource> = Box::new(MyResource{});
let _: Box<dyn ResourceStatic> = Box::new(MyResource{});
but this is?
let _: Box<dyn ResourceInstance> = Box::new(MyResource{});
Can someone please explain what is going on under the hood so that it doesn't seem arbitrary?
Rust playground
What is a trait object? It is
This definition is sufficient to explain why ResourceInstance
works while Resource
and ResourceStatic
don't.
ResourceInstance
trait ResourceInstance {
fn resource_id(&self) -> u64;
}
This trait can be made into an object because even when the concrete type is not known, you can still call resource_id
on a value that implements the trait (by passing it as the self
parameter).
ResourceStatic
trait ResourceStatic {
fn static_id() -> u64;
}
This trait cannot be made into an object, because static_id
can be called without a value, which means in order to call static_id
you must know the concrete type.
For each trait object type (e.g. dyn ResourceStatic
), the compiler automatically generates an implementation of the corresponding trait (ResourceStatic
). This automatic implementation uses the vtable pointer passed as part of the self
type in the trait methods. When there is no self
type there is no vtable pointer and the compiler can't automatically implement that method. There are no "bare vtable pointers" in Rust.
To perhaps understand this better, imagine dyn ResourceStatic
is a valid type. What does <dyn ResourceStatic>::static_id()
do? It cannot defer to the implementation of the concrete type, because there is no value and therefore no concrete type. Shall we conclude that dyn ResourceStatic
doesn't implement ResourceStatic
? That seems obviously wrong. Or does dyn ResourceStatic
have its own implementation of ResourceStatic
that doesn't defer to some concrete type? That also doesn't make sense, because the whole point of dyn ResourceStatic
is to stand in for a concrete type.
The way Rust resolves this problem is simply to reject dyn ResourceStatic
as a type.
Resource
trait Resource {
const RESOURCE_ID: u64;
}
This trait cannot be made into an object for the same reason ResourceStatic
cannot: because it's impossible for the trait object type dyn Resource
to automatically satisfy the requirements of the trait.
If you want dynamic dispatch on the type Self
, you need a self
argument to dispatch on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With