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 sizePut 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?