In the following code I have a simple trait, A, and a struct Foo that implements A...
Next, I define a function that takes a reference to a trait object. From main() I pass in a reference to a concrete Foo. This works fine and successfully calls the method in the trait.
trait A {
fn do_something(&self) {
println!("Doing something.");
}
}
struct Foo {}
impl A for Foo {}
fn main() {
let foo = Foo {};
make_do_something(&foo);
}
fn make_do_something(a: &dyn A) {
a.do_something();
}
What is the Rust compiler doing here? Is the concrete Foo reference automatically coerced to a Trait object reference? That is, is a new trait object being created on the heap that is referring to my concrete object? I can't find a clear description of how this works in the documentation.
Is the concrete Foo reference automatically coerced to a Trait object reference?
Yes, the &Foo
is coerced to &dyn A
. The &Foo
consists of a pointer to the data of the Foo
(which happens to be zero bytes long in your example, but this makes no difference); the &dyn A
consists of that pointer and also a pointer to the vtable generated from impl A for Foo
.
is a new trait object being created on the heap
No; a trait object demands additional data to refer to it, not to store it.
Compare this with the other main kind of dynamically-sized (!Sized
) type in Rust, the slice: a &Foo
contains a pointer to a Foo
, and a &[Foo]
contains that pointer and a length. They might be the same data pointed to (you can convert both ways), but the pointer is different.
We can say in general that pointing to a value of a Sized
type requires only the machine pointer to the data; pointing to a value of a !Sized
type requires additional information (called the āmetadataā).
Every pointer to a dynamically-sized type is constructed either by a coercion, in which case the compiler inserts the necessary data that is known at compile time (vtable for a trait object, or length for a slice pointer coerced from an pointer array), or by library code which knows something about what it's constructing a pointer for (e.g. when Vec<T>
produces a &[T]
).