Course contentsShow
← Rust
Lesson 983 of 1,9109. Smart Pointers and Interior MutabilityFree lesson

Boxed Trait Objects and Type Erasure

Implement polymorphism by wrapping heterogeneous types in trait objects.

In this lesson, you will learn how to bypass Rust's strict static typing to store heterogeneous types in a single collection using Box<dyn Trait>.

The Problem of Homogeneity

Rust’s compiler loves knowing the exact size of everything at compile time. If you try to create a Vec<T>, every element must have the same size and the same concrete type. But what if you are building a graphics engine where you need a single list to hold both Circle and Square structs? You cannot store them directly in a Vec because they have different memory layouts.

Type Erasure via dyn

This is where Type Erasure comes in. By wrapping an object in a Box<dyn Trait>, you are essentially telling the compiler: "I don't care about the specific struct anymore; I only care that this item implements a specific set of behaviors defined in the Trait."

When you use Box<dyn Shape>, the Box performs two tasks:

  1. Heap Allocation: It moves the concrete struct to the heap (addressing the "unknown size" problem).
  2. VTable Pointer: It stores the item as a fat pointer, which includes the address of the data and a pointer to a vtable (virtual method table). This vtable contains the function pointers for the trait methods, allowing the program to perform dynamic dispatch at runtime.

Example

trait Shape {
    fn draw(&self);
}

struct Circle { radius: f64 }
struct Square { side: f64 }

impl Shape for Circle { fn draw(&self) { println!("Drawing circle"); } }
impl Shape for Square { fn draw(&self) { println!("Drawing square"); } }

fn main() {
    // We erase the concrete types (Circle/Square) into the trait type
    let shapes: Vec<Box<dyn Shape>> = vec![
        Box::new(Circle { radius: 2.0 }),
        Box::new(Square { side: 5.0 }),
    ];

    for shape in shapes {
        shape.draw(); // Dispatch happens via the vtable
    }
}

The concrete types are "erased" from the perspective of the Vec. The compiler only knows that the items are things that satisfy Shape. While this introduces a small performance cost due to pointer indirection, it is the fundamental mechanism for achieving polymorphism in Rust.

Key Takeaway: Boxed trait objects decouple your collection from concrete types by moving data to the heap and using a vtable to resolve method calls at runtime.