Rust by Example: Box Smart Pointer
Rust 1.75+
Learn to use Box<T>, the simplest smart pointer for heap allocation. This example demonstrates how to store data on the heap, create recursive types, and manage ownership of large data structures.
Code
fn main() {
// Allocate an integer on the heap
let b = Box::new(5);
println!("b = {}", b);
// Recursive types (Cons List)
// A List is either Cons(i32, List) or Nil
// We MUST use Box because List has infinite size otherwise
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil))))));
println!("Created a recursive list: {:?}", list);
}
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}
/*
Without Box, this would error: "recursive type has infinite size"
enum List {
Cons(i32, List), // Error!
Nil,
}
*/Explanation
Box<T> is the simplest smart pointer. It allows you to store data on the heap rather than the stack. What remains on the stack is a pointer to the heap data. This is useful in three main scenarios:
- Recursive Types: Types like linked lists or trees have an infinite size if stored directly (a node contains a node, which contains a node...). Using
Boxintroduces a pointer, which has a known, fixed size, breaking the cycle. - Large Data Transfer: When you want to transfer ownership of a large amount of data without copying it, moving a
Box(just a pointer) is cheap. - Trait Objects: When you want to own a value and care only that it implements a specific trait, not its concrete type (e.g.,
Box<dyn Error>).
Code Breakdown
3
Box::new(5). This allocates memory on the heap large enough to hold an i32, writes the value 5 into it, and returns a pointer to it. The pointer is stored in b on the stack.15
Box
. This breaks the infinite recursion of type size. A Box always has a known, fixed size (usually 8 bytes on 64-bit systems), regardless of how much data it points to.9
List::Cons(...). We are constructing a linked list. Each node owns the next node via a Box. When list goes out of scope, the entire chain is dropped recursively.
