August 5, 20253 min

What is the purpose of Box<T> in Rust?

m
mayo

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 the Box goes out of scope.
  • Fixed Size: The Box itself is a pointer (usize) with a known stack size, even if T 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 when Box 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 or Arc 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.