Course contentsShow
← Rust
Lesson 719 of 1,9106. Generics and Static DispatchFree lesson

Avoiding Monomorphization Bloat

Use boxing or trait objects sparingly when compilation time becomes a bottleneck.

In this lesson, you will learn how to use trait objects to trade a small amount of runtime performance for significantly faster compilation times and smaller binary sizes.

The Cost of Static Dispatch

When you use generics in Rust, the compiler performs monomorphization. This means every time you call a generic function with a different concrete type, the compiler generates a unique version of that function specifically for that type.

While this is incredibly fast at runtime (because the compiler knows exactly which code to run), it has a downside: if you use a generic function with 50 different types, the compiler generates 50 copies of that code. This leads to "monomorphization bloat," where your compile times skyrocket and your final binary size balloons.

Dynamic Dispatch to the Rescue

When compile time becomes a bottleneck, you can switch from static dispatch (generics) to dynamic dispatch (trait objects). By using &dyn Trait or Box<dyn Trait>, you tell the compiler to look up the method at runtime using a "vtable" instead of generating specialized code for every type.

Compare these two approaches:

// Static dispatch: Compiler generates a copy for every type used
fn process_item<T: Display>(item: T) {
    println!("{}", item);
}

// Dynamic dispatch: Only one version is generated
fn process_item_dyn(item: &dyn Display) {
    println!("{}", item);
}

When to Make the Switch

You shouldn't use trait objects everywhere—generics are generally faster because they allow the compiler to perform optimizations like inlining. However, if you notice your project taking a very long time to compile, or if your binary size is unusually large, look for generic functions that are called with many different types. Replacing them with trait objects allows the compiler to compile the logic once, treating every input through a shared interface.

Think of it as a tool in your kit: use static dispatch (generics) by default for performance-critical hot paths, and reserve dynamic dispatch (trait objects) to keep your codebase manageable when your generic code starts causing "bloat."

Key Takeaway: When monomorphization causes excessive compile times or binary size, use trait objects (&dyn Trait) to force the compiler to generate code only once.