Using Box<T> to Point to Data on the Heap

Box<T> allocates data on the heap with single ownership. Primary use cases:

  • Types with unknown compile-time size (recursive types)
  • Large data that should move without copying
  • Trait objects (covered in Chapter 18)

Basic Usage

fn main() {
    let b = Box::new(5);
    println!("b = {b}");
}

Box<T> provides heap allocation with automatic cleanup when it goes out of scope.

Enabling Recursive Types

Recursive types require indirection since Rust needs to know type sizes at compile time.

enum List {
    Cons(i32, List),
    Nil,
}

fn main() {}

This fails because List has infinite size:

$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0072]: recursive type `List` has infinite size
 --> src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^
2 |     Cons(i32, List),
  |               ---- recursive without indirection
  |
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
  |
2 |     Cons(i32, Box<List>),
  |               ++++    +

error[E0391]: cycle detected when computing when `List` needs drop
 --> src/main.rs:1:1
  |
1 | enum List {
  | ^^^^^^^^^
  |
  = note: ...which immediately requires computing when `List` needs drop again
  = note: cycle used when computing whether `List` needs drop
  = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

Some errors have detailed explanations: E0072, E0391.
For more information about an error, try `rustc --explain E0072`.
error: could not compile `cons-list` (bin "cons-list") due to 2 previous errors

Size Calculation Issue

Non-recursive enums use the size of their largest variant. Recursive types create infinite size calculations since each Cons variant contains another List.

Solution with Box<T>

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

Box<T> has a known size (pointer size), breaking the recursive chain. The heap-allocated data structure achieves the desired recursion without infinite compile-time size.

Box<T> implements Deref for reference-like behavior and Drop for automatic cleanup, making it a true smart pointer.