Monomorphization and inlining vs VTables and fat pointer trait objects (`Box<dyn Trait>`).
trait Drawer {
fn draw(&self);
}
struct Button;
impl Drawer for Button { fn draw(&self) { println!("Drawing Button"); } }
struct Canvas;
impl Drawer for Canvas { fn draw(&self) { println!("Drawing Canvas"); } }
// Dynamic dispatch: Heterogeneous collection using fat pointers
fn render_all(items: &[Box<dyn Drawer>]) {
for item in items {
item.draw(); // Looks up fn pointer in VTable at runtime
}
}
fn main() {
let widgets: Vec<Box<dyn Drawer>> = vec![
Box::new(Button),
Box::new(Canvas),
];
render_all(&widgets);
}Rust lets developers choose explicitly between compile-time code duplication (monomorphization) and runtime dynamic dispatch.
With Generics (`fn draw<T: Drawer>(item: T)`), the compiler generates a specialized binary function for every concrete type used. This allows aggressive LLVM optimization and CPU instruction inlining.
With `dyn Trait`, the value is represented as a fat pointer containing: (1) pointer to the concrete data on heap, and (2) pointer to the compiler-generated VTable containing function pointers and drop glue.
The VTable contains: `[ destructor_ptr | size: usize | align: usize | method_1_ptr | method_2_ptr ... ]`.
// Fat pointer on stack (16 bytes):
// [ data_ptr: 0x1000 | vtable_ptr: 0x8000 ]
// │ │
// ▼ ▼
// [ Button struct ] [ drop_fn | size: 0 | align: 1 | draw_fn ]