In this lesson, you will learn why Rust requires explicit heap allocation to define data structures that contain themselves.
The Problem: Infinite Size
In Rust, the compiler must know the exact size of every type at compile time to allocate memory on the stack. Consider a simple linked list node:
struct Node {
value: i32,
next: Node, // Error!
}
If you try to compile this, the compiler will panic. To understand why, imagine how much space a Node occupies. To know the size of a Node, you must know the size of its next field. But next is a Node, which contains another next, and so on. This creates an infinitely large type, which is impossible to place on the stack.
The Solution: Indirection via Box
To break this infinite chain, we use a Box<T>. A Box is a smart pointer that allocates its contents on the heap. Crucially, the size of a pointer is fixed and known at compile time—it is just the size of a memory address (usually 8 bytes on a 64-bit system).
By wrapping the recursive part in a Box, the compiler no longer needs to know the size of the entire list at once; it only needs to know the size of the pointer to the next one.
struct Node {
value: i32,
next: Option<Box<Node>>,
}
Why This Works
By using Box<Node>, we turn a direct, infinite dependency into a fixed-size reference. The Node struct now contains:
- An
i32(fixed size). - An
Option<Box<Node>>(a pointer, which has a fixed size).
When the program runs, the "next" node is stored somewhere else in heap memory. The Node struct on the stack simply holds the address of that heap location. This indirection is the fundamental way Rust manages recursive data structures, enabling complex patterns like trees, graphs, and linked lists while maintaining strict memory safety and predictable stack usage.
Key Takeaway: Recursive types require the Box smart pointer to provide a known, fixed size to the compiler by moving the recursive portion of the data to the heap.