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
ResourceInstanceworks whileResourceandResourceStaticdon't.ResourceInstanceThis trait can be made into an object because even when the concrete type is not known, you can still call
resource_idon a value that implements the trait (by passing it as theselfparameter).ResourceStaticThis trait cannot be made into an object, because
static_idcan be called without a value, which means in order to callstatic_idyou 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 theselftype in the trait methods. When there is noselftype 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 ResourceStaticis 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 ResourceStaticdoesn't implementResourceStatic? That seems obviously wrong. Or doesdyn ResourceStatichave its own implementation ofResourceStaticthat doesn't defer to some concrete type? That also doesn't make sense, because the whole point ofdyn ResourceStaticis to stand in for a concrete type.The way Rust resolves this problem is simply to reject
dyn ResourceStaticas a type.ResourceThis trait cannot be made into an object for the same reason
ResourceStaticcannot: because it's impossible for the trait object typedyn Resourceto automatically satisfy the requirements of the trait.TL;DR
If you want dynamic dispatch on the type
Self, you need aselfargument to dispatch on.