Course contentsShow
← Rust
Lesson 279 of 1,9103. Borrowing and ReferencesFree lesson

The Cost of Dynamic Dispatch

Evaluate the performance of trait object references in high-throughput systems.

In this lesson, you will learn how trait objects affect performance in high-throughput systems by analyzing the invisible overhead of dynamic dispatch.

When you use a reference to a trait object—like &dyn Animal—Rust cannot know the concrete type (e.g., Dog or Cat) until the program is actually running. Because the compiler doesn't know the exact size or method implementation at compile time, it cannot "inline" the code. Instead, it must use dynamic dispatch.

The Mechanism: Vtables

To handle this, Rust creates a vtable (virtual method table) for the type. A trait object reference is actually a "fat pointer"—a pair of two pointers: one to the data itself, and one to the vtable. When you call a method on &dyn Animal, the CPU must perform these extra steps:

  1. Access the fat pointer.
  2. Read the pointer to the vtable.
  3. Look up the memory address of the specific method within that vtable.
  4. Perform an indirect jump to execute that address.

The Performance Hit

In high-throughput systems, this process introduces two primary costs:

  • Indirection: The CPU has to perform multiple memory lookups before it even starts executing your code.
  • Lost Optimization: Because the compiler doesn't know which code will run, it cannot perform inlining. Inlining is a critical optimization where the compiler replaces a function call with the actual function body, allowing it to optimize the code as a single block. With dynamic dispatch, the "black box" of the vtable prevents this.

Consider a hot loop processing millions of items:

// Static Dispatch (Fast - compiler inlines this)
fn process_fast<T: Animal>(item: &T) { item.speak(); }

// Dynamic Dispatch (Slower - CPU must perform vtable lookup)
fn process_slow(item: &dyn Animal) { item.speak(); }

In a simple application, this cost is negligible. However, in high-throughput systems—like game engines or low-latency network protocols—calling a vtable lookup millions of times per second can lead to significant latency compared to static dispatch, where the compiler resolves the method address during the build process.

Key Takeaway: Dynamic dispatch trades raw execution speed and inlining potential for the flexibility of polymorphic references; use it sparingly in your most performance-critical loops.