Course contentsShow
← Rust
Lesson 520 of 1,9105. Type System and TraitsFree lesson

Static vs Dynamic Dispatch Trade-offs

Benchmark and choose between monomorphization and trait objects for performance-critical components.

In this lesson, you will learn how to evaluate the performance trade-offs between monomorphization (static dispatch) and trait objects (dynamic dispatch) when designing high-performance components.

Static Dispatch (Monomorphization)

When you use a generic type like fn process<T: Trait>(item: T), Rust uses monomorphization. The compiler generates a unique version of that function for every concrete type you use.

  • Pros: The compiler sees the exact type at compile time. This allows it to "inline" the function code, removing the overhead of a function call and enabling deep optimizations.
  • Cons: It increases binary size because the code is duplicated for every type used.

Dynamic Dispatch (Trait Objects)

When you use a trait object like &dyn Trait, Rust uses dynamic dispatch. Instead of specialized code, the compiler generates a "vtable" (a virtual method table). At runtime, the program performs a pointer lookup to find the correct method to execute.

  • Pros: It keeps your binary small and provides flexibility, as you can store different types that implement Trait in the same collection (e.g., Vec<Box<dyn Trait>>).
  • Cons: The indirection prevents the compiler from inlining the method call. If this happens inside a tight loop, the performance penalty of that constant pointer lookup can become significant.

Choosing Your Approach

Consider this scenario:

// Static: High performance, but forces a single type per call site
fn run_static<T: Display>(val: T) { println!("{}", val); }

// Dynamic: Flexible, but incurs vtable lookup cost
fn run_dynamic(val: &dyn Display) { println!("{}", val); }

In performance-critical code, such as tight mathematical loops or rendering engines, static dispatch is almost always preferred because it allows the compiler to optimize the entire call chain. Use dynamic dispatch only when you need to store heterogeneous types in a single structure or when the overhead of a vtable lookup is negligible compared to the work being performed by the function itself.

Key Takeaway: Prefer static dispatch for CPU-bound hot paths to enable compiler inlining, and reserve dynamic dispatch for scenarios requiring runtime flexibility or reduced binary size.