Skip to content

Trait Objects

Rust gives you two ways to use trait polymorphism:

  • Static dispatch (via generics): the compiler knows the exact type at compile time and generates specialized code for each one. Zero runtime overhead.
  • Dynamic dispatch (via dyn Trait): the concrete type is not known until runtime. The compiler stores a vtable — a table of function pointers — alongside the value so the right method can be found at runtime.

When you write dyn Animal, you are asking Rust to use dynamic dispatch. Because the size of the concrete type is unknown at compile time, you must put a dyn Trait behind a pointer. The two most common forms are:

  • &dyn Trait — a borrowed trait object (no heap allocation)
  • Box<dyn Trait> — an owned trait object stored on the heap
fn make_sound(animal: &dyn Animal) {
println!("{} says: {}", animal.name(), animal.sound());
}

This function accepts any type that implements Animal — you do not need to know the concrete type at compile time.

The most powerful use case for trait objects is storing values of different concrete types together in a Vec or other collection. With generics alone this is impossible — a Vec<T> can only hold one concrete type at a time.

let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
Box::new(Dog),
];

Each element is a Box<dyn Animal> — an owned pointer to some type that implements Animal. The vtable stored alongside each value ensures the right sound() and name() implementations are called.

Not every trait can be used as a dyn Trait. A trait is object-safe if:

  1. It has no methods that return Self.
  2. It has no generic type parameters on its methods.

The standard library traits Display, Debug, and Iterator are object-safe. Traits like Clone (which returns Self) are not.

ApproachType known at compile time?Runtime costHeterogeneous collection?
Generic (impl Trait or T: Trait)YesZeroNo
Trait object (dyn Trait)NoSmall vtable indirectionYes

Use generics when you know the types at compile time and want maximum performance. Use trait objects when you need to work with a collection of mixed concrete types or when the type is determined by user input or configuration at runtime.

trait Animal {
fn sound(&self) -> &str;
fn name(&self) -> &str;
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn sound(&self) -> &str { "Woof" }
fn name(&self) -> &str { "Dog" }
}
impl Animal for Cat {
fn sound(&self) -> &str { "Meow" }
fn name(&self) -> &str { "Cat" }
}
fn make_sound(animal: &dyn Animal) {
println!("{} says: {}", animal.name(), animal.sound());
}
fn main() {
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog),
Box::new(Cat),
Box::new(Dog),
];
for animal in &animals {
make_sound(animal.as_ref());
}
}
What does `dyn Trait` mean in Rust?
What is a vtable?
Why must `dyn Trait` be placed behind a pointer such as `Box<dyn Trait>` or `&dyn Trait`?
Which approach allows storing values of different concrete types together in a `Vec`?