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

Trait object performance overhead

Measure the costs of vtable indirection in high-frequency invocation loops.

In this lesson, you will learn why using &dyn Trait incurs a small performance penalty compared to generic static dispatch.

When you use generics (e.g., fn process<T: Trait>(item: T)), the Rust compiler performs monomorphization. It creates a specific version of the function for every concrete type you use. Because the compiler knows the exact type at compile time, it can inline the function calls directly, resulting in highly optimized assembly code with no overhead.

However, when you use trait objects (&dyn Trait), the compiler loses knowledge of the concrete type. Instead, it must use dynamic dispatch. This involves two specific costs:

1. The Vtable Indirection

A trait object is a "fat pointer." It contains two pointers: one to the instance of your data and one to a vtable (virtual method table). The vtable contains the memory addresses of the trait's methods for that specific type. When you call a method on a trait object, the CPU must first look up the address in the vtable before it can execute the code.

2. The Loss of Inlining

Because the method address is determined at runtime, the compiler cannot "see" inside the function call to inline it. In high-frequency loops—where a function might be called millions of times—the inability to inline the code and the extra memory lookups for the vtable will significantly degrade performance.

Consider this loop:

// Static dispatch (Fast): Compiler inlines the logic.
for item in &items { item.perform(); }

// Dynamic dispatch (Slower): CPU performs pointer chasing for every iteration.
for item in &trait_objects { item.perform(); }

In a tight loop, the "pointer chasing" required by the vtable prevents the CPU from optimizing the execution flow as effectively as it would with static dispatch. While trait objects are incredibly flexible for building heterogeneous collections, they trade away the "zero-cost" nature of generics. Always prefer generics when performance is critical, and reserve dyn for scenarios where you need to store mixed types in a single collection.

Key Takeaway: Trait objects force dynamic dispatch via vtable lookups, which prevents compiler inlining and adds a small but measurable overhead to high-frequency execution loops.