We use dyn Trait
over (data, vtable)
for the same reason we use, references over pointers, Rc
over (pointer_data, pointer_counter)
, Box
over malloc
/free
. It's a safe abstraction which can be proven to not cause errors.
A few reasons:
-
(data, vtable)
layout is unstable fordyn Trait
. -
Vtable
could potentially point to the wrong type's vtable if you set it in (safe) code:let mut my_dyn: (*const (), *const VTable) = (&2usize as *const usize as *const _, VTable::usize_table()); my_dyn.1 = VTable::string_table(); unsafe { (my_dyn.1.print)(my_dyn.0) }; // Uh oh!
- As seen above, there are potentially unsafe uses of the type, and then we'd end up at a safe abstraction over a potentially unsafe thing... oh wait; we're back to
dyn Trait
! - You, as the programmer, cannot directly access the vtable for a type without unsafe and going through an unstable structure (The layout of
(data, vtable)
is once again, unstable). - How would I go about trying to make sure the destructor is run?
-
dyn Trait
is a static type and can be checked at compile time. While it may be called a "runtime type", it can still be statically checked, and therefore make the type system stronger.
The main reason to not use dynamic dispatch in general is that there is a performance loss by going through several pointers (The data, the vtable and then the function).
If your structure cannot be expressed using the standard dyn Trait
structure, then restructuring your program should be your first try, and then developing your own (data, pointer)
structure once you've convinced yourself you cannot go forward without it.
In most cases, generics are fine.