C.W.K.
Stream
Lesson 01 of 05 · published

Box — A Value on the Heap

~11 min · smart-pointers, box, recursive-types

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Smart pointers are types that act like a pointer but add a capability — ownership, reference counting, or interior mutability. The simplest is Box<T>: a value that lives on the heap.

Box: a value on the heap

Box<T> stores its T on the heap and holds a pointer to it on the stack. It owns the value (drops it when the Box drops) and otherwise behaves like the value itself. Most of the time you don't need it — values live happily on the stack. You reach for Box in three specific situations.

When you need a Box

One: a recursive type, like a tree node that contains itself — without indirection its size would be infinite, so Box breaks the cycle. Two: a large value you want to move cheaply (move the pointer, not the bytes). Three: a trait object, Box<dyn Trait>, which you met in the Traits track — a fixed-size handle to a differently-sized concrete type.

Box is the simplest smart pointer: owned, heap-allocated, single-owner. It adds heap storage and nothing else — the same ownership rules as a plain value, just living on the heap. It's the foundation the fancier pointers (Rc, RefCell, Arc) build on.

The recursive type classic

A type like enum List { Cons(i32, List), Nil } won't compile — Rust can't compute its size because it nests forever. Wrap the recursive field in a BoxCons(i32, Box<List>) — and the size becomes 'an i32 plus a pointer,' finite and known. This is the textbook reason Box exists.

Code

Box for a recursive type, and for a plain heap value·rust
// A recursive type needs indirection or its size is infinite:
enum List {
    Cons(i32, Box<List>), // Box: a fixed-size pointer to the next node
    Nil,
}

use List::{Cons, Nil};

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

    // Box also simply puts any value on the heap:
    let boxed = Box::new(42);
    println!("{}", *boxed); // deref to read the value

    if let Cons(head, _) = list {
        println!("head: {head}");
    }
}

External links

Exercise

Define a recursive binary-tree node enum where each node holds an i32 and two child subtrees. Without Box, watch it fail to compile (infinite size). Add Box around the children and build a small three-node tree. Why does Box make the size finite?
Hint
A Box is always pointer-sized regardless of what it points at. So Box<Node> is a fixed size even though Node contains more Nodes — the recursion goes through a pointer, not inline data.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.