Table of contents
Box<T> is a smart pointer in Rust that provides heap allocation for a value of type T. It is the simplest way to store data on the heap, offering ownership and memory safety guarantees without runtime overhead.
What is Box?
- Heap Allocation: Moves data from the stack to the heap.
let x = Box::new(42); // `42` is stored on the heap - Ownership:
Box<T>owns the data and ensures it is dropped when theBoxgoes out of scope. - Fixed Size: The
Boxitself is a pointer (usize) with a known stack size, even ifTis dynamically sized (e.g.,Box<dyn Trait>).
When to Use Box
1. Recursive Types (e.g., Linked Lists)
Rust requires compile-time-known sizes, but recursive types (like trees or lists) would be infinitely sized without indirection.
enum List {
Cons(i32, Box<List>), // Without `Box`, this would be invalid
Nil,
}
2. Large Data (Avoid Stack Overflow)
Moving large structs (e.g., a 1MB buffer) to the heap prevents stack overflows.
let big_data = Box::new([0u8; 1_000_000]); // Heap-allocated array
3. Trait Objects (dyn Trait)
Storing heterogeneous types behind a trait interface for dynamic dispatch.
trait Animal { fn speak(&self); }
struct Cat;
impl Animal for Cat { fn speak(&self) { println!("Meow"); } }
let animals: Vec<Box<dyn Animal>> = vec![Box::new(Cat)]; // Dynamic dispatch
4. Transferring Ownership Across Threads
Box can be used with std::thread::spawn to move owned data to another thread.
let x = Box::new(42);
std::thread::spawn(move || {
println!("{}", x); // `x` is moved into the thread
});
How Box Differs from Other Pointers
| Type | Ownership | Use Case |
|---|---|---|
Box<T> |
Owned (unique) | Heap allocation, recursive types |
&T/&mut T |
Borrowed | Temporary references |
Rc<T> |
Shared (reference-counted) | Multiple owners in single-threaded code |
Arc<T> |
Shared (atomic refcount) | Thread-safe multiple owners |
Memory Safety Guarantees
- No manual
free(): Automatically deallocates whenBoxgoes out of scope. - No null pointers:
Boxcannot be null (unlike raw pointers). - No leaks: Compiler enforces ownership rules.
Example: Box vs Stack Allocation
// Stack (fails if too large)
// let arr = [0u8; 10_000_000]; // Likely stack overflow
// Heap (works)
let arr = Box::new([0u8; 10_000_000]); // Safe
Key Takeaways
✅ Use Box<T> when you need:
- Heap allocation for large or recursive data.
- Trait objects (
dyn Trait). - Explicit ownership with a fixed-size pointer.
🚫 Avoid if:
- You only need a reference (
&T). - You need shared ownership (use
RcorArcinstead).
Thought Experiment: What happens if you try to Box a value already on the heap?
Answer: It’s fine—just adds another pointer indirection, as the Box will point to the new heap allocation.