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?
What is a trait object? It is
This definition is sufficient to explain why
ResourceInstance
works whileResource
andResourceStatic
don't.ResourceInstance
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 theself
parameter).ResourceStatic
This trait cannot be made into an object, because
static_id
can be called without a value, which means in order to callstatic_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 theself
type in the trait methods. When there is noself
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 thatdyn ResourceStatic
doesn't implementResourceStatic
? That seems obviously wrong. Or doesdyn ResourceStatic
have its own implementation ofResourceStatic
that doesn't defer to some concrete type? That also doesn't make sense, because the whole point ofdyn 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
This trait cannot be made into an object for the same reason
ResourceStatic
cannot: because it's impossible for the trait object typedyn Resource
to automatically satisfy the requirements of the trait.TL;DR
If you want dynamic dispatch on the type
Self
, you need aself
argument to dispatch on.