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

Type Erasure vs Type Preservation

Decide when to erase types for convenience versus preserving them for maximum optimization.

In this lesson, you will learn how to choose between the flexibility of type erasure and the raw speed of type preservation when designing generic systems.

The Core Concept

In Rust, Type Preservation (Static Dispatch) keeps the concrete type information available at compile time. When you use generics like fn process<T: Trait>(item: T), the compiler generates a unique, optimized version of that function for every specific type used. This allows for constant folding, inline expansion, and elimination of branch overhead.

Type Erasure (Dynamic Dispatch), on the other hand, uses trait objects like &dyn Trait. Here, the compiler "erases" the specific type information, replacing it with a pointer to the data and a pointer to a vtable (a table of function pointers).

Deciding Which to Use

Use Type Preservation when:

  • Performance is critical: If your code is inside a hot loop (like a compression algorithm or iterator chain), you want the compiler to see the concrete type to inline logic and remove indirection.
  • Size is a concern: You don't need a massive binary filled with monomorphized copies of the same function.

Use Type Erasure when:

  • You need heterogenous collections: If you want a Vec<Box<dyn Trait>> that holds different structs (e.g., a list of different UI widgets), type erasure is your only option.
  • You want to reduce compile times: Monomorphization (Type Preservation) forces the compiler to re-generate code for every type, which can significantly increase your build duration.

A Quick Illustration

// Preservation: Fast, but forces you to know the type at compile time
fn handle_static<T: Display>(item: T) { println!("{}", item); }

// Erasure: Flexible, allows storing different types in a list
fn handle_dynamic(item: &dyn Display) { println!("{}", item); }

let list: Vec<Box<dyn Display>> = vec![Box::new(10), Box::new("Hello")];

By erasing the type, you gain the ability to store a String and an i32 in the same vector. By preserving it, you gain the ability for the CPU to execute the println! logic with zero pointer-chasing overhead.

Key Takeaway: Choose Type Preservation for high-performance, predictable code paths; use Type Erasure when your architecture requires heterogeneous storage or modular flexibility.