In this lesson, you will learn why accessing heap-allocated data through a pointer (indirection) is slower than accessing data directly on the stack.
Direct Access (Stack)
When you store a simple value like an i32 directly on the stack, the CPU knows exactly where that data lives. It is located at a fixed offset from the stack pointer. When your code needs the value, the CPU performs a single instruction to read that memory address. Because the data is "right there" in the current execution context, it is extremely fast and friendly to the CPU’s prefetcher.
The Heap Wrapper (Indirection)
When you use a wrapper like Box<T>, you are placing the actual data on the heap. Your stack variable no longer holds the data; it only holds a pointer—a memory address—pointing to the heap.
To retrieve the value, the CPU must now perform two distinct steps:
- Dereference: Read the memory address stored in your stack variable.
- Fetch: Go to that new memory address (in the heap) to finally retrieve the actual data.
This is "indirection." Instead of one direct read, you have a chain of lookups.
// Stack allocation (fast, direct)
let x: i32 = 42;
// Heap allocation (indirection)
let y: Box<i32> = Box::new(42);
Why it Matters
The real cost isn't just the extra instruction; it is cache pressure. When data is on the stack, it is likely already in the L1 cache. When you follow a pointer to the heap, that memory address might not be in the cache at all. This forces the CPU to wait for a "cache miss," which is significantly slower than reading from the stack.
While Box is necessary for ownership and dynamic sizing, it introduces a "pointer chase." Every time you add a layer of indirection (like Box<Box<T>> or nested pointers), you increase the likelihood that the CPU will have to stall while waiting for data to arrive from slower memory tiers.
Key Takeaway: Indirection replaces a direct memory access with a pointer dereference, which forces the CPU to perform additional lookups and increases the risk of costly cache misses.