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 theBox
goes out of scope. - Fixed Size: The
Box
itself is a pointer (usize
) with a known stack size, even ifT
is 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 whenBox
goes out of scope. - No null pointers:
Box
cannot 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
Rc
orArc
instead).
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.