🎓 A1Learner
🔍
0 of 149 lessons completed

Rust Box

Box is a small handle that holds a value on the heap, the program's big storage area. The Box handle keeps one fixed size, even for a list that contains itself.

A list that contains itself, with no Box

Without a pointer, the type must contain itself forever, and Rust refuses it:

enum List {
    Cons(i32, List),
    Nil,
}
// error[E0072]: recursive type `List` has infinite size

Put a value on the heap

Box::new moves the value onto the heap and hands you a small pointer to it. Reading through the Box is automatic: *boxed gives the value, and method calls just work.

let boxed = Box::new(5);
println!("{}", *boxed);

A list that contains itself

A recursive type needs the Box. Without it, the type would contain itself forever and Rust refuses it with error E0072 (checked). This is indirection: storing a pointer to a value instead of the value itself.

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

A Box is one pointer, however big the value

A Box is never empty — it always points at a real value, never at address zero. That never-used address is called a niche: a pattern the type cannot normally hold. Rust stores the None case of Option<Box<T>> in that niche, so the Option form costs nothing extra.

println!("box = {}", std::mem::size_of::<Box<i32>>());
println!("option box = {}", std::mem::size_of::<Option<Box<i32>>>());

A Box stays small around a huge value

The array takes 4,096 bytes. The Box that holds it takes 8:

println!("array = {} bytes", std::mem::size_of::<[u8; 4096]>());
println!("boxed array = {} bytes", std::mem::size_of::<Box<[u8; 4096]>>());

Move the value back out

Use *box to copy or move the plain value back out of the Box when you need it again.

let b = Box::new(42);
let v = *b;
println!("v = {}", v);

Example

enum List { Cons(i32, Box<List>), Nil }
use List::{Cons, Nil};
fn sum(list: &List) -> i32 { match list { Cons(n, rest) => n + sum(rest), Nil => 0 } }
fn main() { let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); println!("sum = {}", sum(&list)); }

Key points

  • A Box is one small pointer; the value lives on the heap behind it.
  • A Box has exactly one owner, just like a plain value
  • Box<dyn Trait> holds any type that implements the trait — the trait objects from the Trait Bounds lesson
  • Use Box for a large value you hand over often.
  • Moving the Box copies only the small pointer.
  • The big data sitting on the heap stays put.
  • Use Box::leak when a value must live until the programme ends.
  • It hands back a &'static reference.
  • It never frees the value.
  • It adds no counting and no sharing — reach for it when the problem is size or storage, not shared ownership.

In this example, List is an enum for a linked list. Each Cons holds a number and the rest of the list. That rest is a Box<List> — a pointer — so every Cons has a known size. Without the Box, the type would contain itself forever and Rust would refuse it. sum walks the list by matching. A Cons adds its number to the sum of the rest. Nil adds 0. main builds 1, 2, 3 chained in boxes, and sum prints 6.

Note

Box is the simplest smart pointer.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why does a recursive enum need a Box?