Course contentsShow
← Rust
Lesson 422 of 1,9104. Lifetimes and Advanced OwnershipFree lesson

Stack-Allocated Traits

Perform stack-based trait allocation to avoid heap overhead for dynamic dispatch.

In this lesson, you will learn how to achieve dynamic dispatch without relying on heap allocation by using stack-allocated trait objects.

The Problem with Box<dyn Trait>

Usually, when you want to use a trait dynamically, you use Box<dyn Trait>. This performs a heap allocation. While flexible, this adds overhead and can be problematic in performance-critical paths where you need cache-friendly memory layouts or are working in environments without an allocator.

The Solution: Stack Trait Objects

If you know the maximum size an object might take or if you only need the trait object to live for a short, scoped duration, you can hold the trait object directly on the stack. Instead of a pointer to the heap, you create a reference to a concrete type that "coerces" into a trait object.

Consider this scenario:

trait Processor {
    fn run(&self);
}

struct FastTask;
impl Processor for FastTask {
    fn run(&self) { println!("Running fast..."); }
}

fn execute(task: &dyn Processor) {
    task.run();
}

fn main() {
    let task = FastTask;
    // We pass a reference, avoiding the heap entirely!
    execute(&task);
}

By passing &task to a function expecting &dyn Processor, Rust performs "unsized coercion." The compiler creates a fat pointer (a pointer to the data and a pointer to the vtable) on the stack. No memory is moved to the heap; the processor simply treats your stack-allocated FastTask as a dyn Processor for the duration of the function call.

Managing Lifetimes

Because stack-allocated trait objects are tied to the scope of the variable, they naturally respect the borrow checker. This is excellent for "Data Locality and Cache Efficiency" (Lesson 4), as your data stays within the local cache lines instead of jumping to random heap addresses.

The primary trade-off is that you cannot return these objects from a function unless you use "Lifetime-Invariant Containers" (Lesson 17) or return a concrete type, as a stack-based trait object's address becomes invalid the moment its owner goes out of scope.

Key Takeaway: You can perform dynamic dispatch on the stack by using references to trait objects, completely bypassing the heap and keeping your data strictly within local, high-speed memory.