Since it doesn't know the concrete type of the data, it only contains a vtpr of dyn Trait, How does it drop itself when it goes out of scope? Does every virtual table in Rust contains a drop method implementation?
The dyn keyword is used when declaring a trait object: The size of a trait is not known at compile-time; therefore, traits have to be wrapped inside a Box when creating a vector trait object.
The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed. That is, the type has been erased.
A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.
The reason why the code above fails is that even though Box holds the trait implementation on the heap, using clone on a Box copies the object rather than a fat pointer to it. That's why the compiler says that our objects must be Sized if we want to use the clone method.
A Trait Object represents a pointer to some concrete type that implements a Trait (think interface if you are unfamiliar with the term Trait ). Trait Objects are Dynamically Sized Types, and because Rust needs to know everything at compile time about the size of the types it works with, Trait Objects are handled a bit differently.
The goal is to highlight some of the differences between Box<T> ( Heap allocated object) and Rc<T> ( Reference Counting pointer ), which are both very important container types you should master early on. Box<T> is a container type designed to allocate and "hold" an object on the heap.
When the concrete type the original Box
contained is unsized into a trait object, the Drop
implementation for the type goes into the vtable. A pointer (Any pointer-like thing in Rust. IE, a reference, Box
, raw pointer, etc.) whose pointee is a trait object is laid out as follows in memory*:
struct FooTraitDynPointer {
ptr: *[const/mut] (),
vtable: &'static VTableImplForFooTrait
}
The ptr
field in my example points to the actual data. We could say that's the original Box
.
The vtable
field in my example points to a static vtable. Say we have the following Foo
trait:
trait Foo {
fn bar(&self) -> usize;
}
Our vtable will look as follows*:
struct VTableImplForFooTrait {
dropper: unsafe fn(*mut ()),
size: usize,
align: usize,
bar: unsafe fn(*const ()) -> usize,
}
We see there, that the drop
is there. Along with it, there're size and align fields which allow owning types to deallocate enough memory. Or re-allocate enough memory.
Here's an example program which crudely extracts the size of a struct from within a pointer to a trait object:
#![feature(raw)]
trait Foo {
fn bar(&self) -> usize;
}
struct Baz {
field: f64
}
impl Foo for Baz {
fn bar(&self) -> usize {
self.field as usize
}
}
#[derive(Clone)]
struct FooVTable {
dropper: unsafe fn(*mut ()),
size: usize,
align: usize,
bar: unsafe fn(*const ()) -> usize,
}
fn main() {
use std::{mem, raw};
let value = Baz { field: 20.0 };
let boxed = Box::new(value) as Box<dyn Foo>;
let deconstructed: raw::TraitObject = unsafe { mem::transmute(boxed) };
let vtable = deconstructed.vtable as *mut FooVTable;
let vtable = unsafe { (*vtable).clone() };
println!("size: {}, align: {}", vtable.size, vtable.align);
let result = unsafe { (vtable.bar)(deconstructed.data) };
println!("Value: {}", result);
}
Playground
(Currently) prints:
size: 8, align: 8
Value: 20
However this may very well change in the future so I'm leaving this timestamp here for someone who reads this in a future where the behaviour has been changed. June 5, 2020.
*: The layout of trait objects, and especially their vtables is NOT guaranteed, so do not rely in actual code.
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