Trait objects enable dynamic dispatch - deciding which method to call at runtime.
// Static dispatch (monomorphization)
fn notify(item: &impl Summary) { ... }
// Dynamic dispatch (trait object)
fn notify(item: &dyn Summary) { ... }trait Draw {
fn draw(&self);
}
let shapes: Vec<Box<dyn Draw>> = vec![
Box::new(Circle { radius: 5.0 }),
Box::new(Rectangle { width: 10.0, height: 5.0 }),
];Trait objects use Box<dyn Trait> because the size is unknown at compile time. The Box provides heap allocation with a known pointer size.
| Use Case | Approach |
|---|---|
| Homogeneous collection | Vec<T> with generics |
| Heterogeneous collection | Vec<Box<dyn Trait>> |
| Plugin systems | Trait objects |
| Maximum performance | Generics (static dispatch) |
Not all traits can be made into trait objects. A trait is object-safe if:
Self in method return typesSpeak trait for Cat (return "Meow!")Vec<Box<dyn Speak>> with a Dog and CatDog.speak() → "Woof!"Cat.speak() → "Meow!"Woof! then Meow!impl Speak for Cat {
fn speak(&self) -> String {
String::from("Meow!")
}
}
fn main() {
let animals: Vec<Box<dyn Speak>> = vec![
Box::new(Dog),
Box::new(Cat),
];
for animal in &animals {
println!("{}", animal.speak());
}
}