100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Rust

Trait Objects and Dynamic Dispatch in Rust

Trait objects like `dyn Trait` enable runtime polymorphism for heterogeneous collections, at the cost of dynamic dispatch.

Traits & GenericsAdvanced10 min readJul 8, 2026
Analogies

Introduction

Generics use static dispatch, resolved at compile time via monomorphization. Sometimes you need to store or pass around values of different concrete types that share a trait, decided only at runtime — for example, a vector of different shapes that all implement a Draw trait. Rust solves this with trait objects, written as dyn Trait, which use dynamic dispatch through a vtable instead of generating specialized code per type.

🏏

Cricket analogy: Static dispatch via generics is like a team practicing a pre-planned play for a specific known bowler; dynamic dispatch with dyn Draw is like a fielding captain deciding the field placement on the fly at runtime for whichever batter is actually at the crease, chosen from a mixed lineup of styles.

Syntax

rust
trait Draw {
    fn draw(&self);
}

fn render(item: &dyn Draw) {
    item.draw();
}

let shapes: Vec<Box<dyn Draw>> = vec![
    Box::new(Circle {}),
    Box::new(Square {}),
];

Explanation

&dyn Trait and Box<dyn Trait> are trait objects: fat pointers that carry both a pointer to the data and a pointer to a vtable of method implementations. Calling a method on a trait object looks up the function in the vtable at runtime, which costs an indirect call compared to the direct, inlined call generics produce. Trait objects must be 'object safe', which roughly means the trait has no generic methods and no methods that return Self by value, since the concrete size and type are erased. Box<dyn Trait> is commonly used to store heterogeneous collections of types that all implement the same trait.

🏏

Cricket analogy: Box<dyn Draw> is like a fat envelope handed to the umpire containing both a player's ID card and a rulebook page telling the umpire which sport's rules apply; consulting that rulebook at every decision costs a beat longer than an umpire who already knows the one sport being played.

Example

rust
trait Draw {
    fn draw(&self);
}

struct Circle;
struct Square;

impl Draw for Circle {
    fn draw(&self) {
        println!("Drawing a circle");
    }
}

impl Draw for Square {
    fn draw(&self) {
        println!("Drawing a square");
    }
}

fn main() {
    let shapes: Vec<Box<dyn Draw>> = vec![Box::new(Circle), Box::new(Square)];
    for shape in shapes.iter() {
        shape.draw();
    }
}

Output

text
Drawing a circle
Drawing a square

Key Takeaways

  • dyn Trait enables dynamic dispatch via a vtable, resolved at runtime.
  • Generics use static dispatch (monomorphization), trait objects use dynamic dispatch — a real tradeoff in performance vs flexibility.
  • Box<dyn Trait> or &dyn Trait allow storing heterogeneous types that share a trait in one collection.
  • A trait must be 'object safe' to be used as a trait object — no generic methods, no returning Self by value.
  • Trait objects are fat pointers: data pointer plus vtable pointer.

Practice what you learned

Was this page helpful?

Topics covered

#Rust#RustProgrammingStudyNotes#Programming#TraitObjectsAndDynamicDispatchInRust#Trait#Objects#Dynamic#Dispatch#OOP#StudyNotes#SkillVeris