What
Rust trait object layout differs critically from C++. In Rust, dyn Trait is a dynamically sized type whose layout is the underlying value. &dyn Trait is a fat pointer: (ptr_to_value, ptr_to_vtable). Box<dyn Trait> is an owned fat pointer with vtable via CoerceUnsized magic. The vtable is never embedded in the struct (unlike C++ where every object pays the cost) — it lives in the fat reference or Box.
The surprising coercion: generic last-field unsized coercion. If a struct has a generic as its LAST field, you can coerce &Struct<ConcreteType> to &Struct<dyn Trait>. Example: struct Adapter<R: ?Sized> { buf: Vec<u8>, inner: R } — let r: &Adapter<dyn Read> = &a; is legal because inner is the last field. This only works with a single generic — you cannot coerce two trait params. And Box/Rc do not support inline generic coercion through references: let r: &Box<dyn Read> = &b; is illegal, while let r: Box<dyn Read> = b; (ownership transfer) is legal.
Why this matters in b00t: DatumStore trait uses Box<dyn DatumStore> extensively. When wrapping adapters (e.g., CachingStore<HashMapStore>), the last-field coercion means &CachingStore<dyn DatumStore> is legal — useful for zero-cost adapter stacks. The Chalk Interner pattern relies on this layout guarantee.
When to Use
Load this skill when working with Rust trait objects, DST coercions, vtable internals, or the DatumStore adapter stack in b00t. It is a prerequisite for datum-macro.
How
- Understand the fat pointer layout:
(data_ptr, vtable_ptr)for&dyn TraitandBox<dyn Trait>. - Remember: vtable is NEVER in the struct in Rust — unlike C++.
- For generic last-field coercion: ensure the generic parameter is the last field of the struct.
- Use
&Struct<dyn Trait>for zero-cost adapter stacks; useBox<dyn Trait>for owned trait objects. - Be aware:
&Box<dyn Trait>is not legal — useBox<dyn Trait>directly for ownership transfer.