RustArena

Step 1 of 7

Box: a value on the heap

A smart pointer is a value that points at data stored somewhere else (like a reference does) but also has extra powers, such as owning that data and cleaning it up. You've used two already without the name: String and Vec<T> each own a buffer on the heap. This module covers the smart pointers you reach for on purpose: Box<T>, Rc<T>, RefCell<T>, Cell<T> and Weak<T>.

Box

Box::new(value) moves the value onto the heap. The Box itself is just a pointer (8 bytes on a 64-bit machine) that can live on the stack, in a struct, anywhere. The Box owns the value: when the Box goes out of scope, the value is dropped and the heap memory is freed. No manual free, no garbage collector.

fn main() {
    let score = Box::new(41);
    let next = *score + 1; // * follows the pointer to the value inside
    println!("score = {score}, next = {next}");

    let names = Box::new(vec!["Ferris", "Corro"]);
    println!("{} names, first is {}", names.len(), names[0]);
}
score = 41, next = 42
2 names, first is Ferris

When do you need a Box? Mostly in three situations:

  1. A type that contains itself (a recursive type), like a list or a tree. This step and the next.
  2. A trait object, Box<dyn Trait>, which you met in the trait objects module.
  3. A very large value you want to move around cheaply (moving a Box copies only the pointer). This one is rare.

Recursive types need a pointer

Here's a linked list: each node holds a number and the rest of the list.

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

fn main() {
    let _empty = List::Nil;
}
error[E0072]: recursive type `List` has infinite size

Rust stores an enum value in one block of memory big enough for its largest variant. Cons contains a whole List, which could be a Cons containing a whole List, and so on: the size would be infinite. The compiler's help line says it all: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle. A Box<List> is a pointer, and a pointer has the same size whatever it points to:

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

use List::{Cons, Nil};

fn sum(list: &List) -> i32 {
    match list {
        Cons(value, rest) => value + sum(rest),
        Nil => 0,
    }
}

fn main() {
    // 3 -> 5 -> 8 -> end
    let list = Cons(3, Box::new(Cons(5, Box::new(Cons(8, Box::new(Nil))))));
    println!("sum = {}", sum(&list));
}
sum = 16

Piece by piece:

Common mistakes

Your task

Fill the blanks. The Item variant needs a boxed rest-of-the-shelf, and building the shelf needs to box the inner value. The program prints total stock: 12.

Challenge 2

The list type and a helper that builds a list from a slice are written for you. Write two recursive functions:

Challenge 3

A delivery route is a chain of stops. Using the Route enum below, write:

Use recursion for both (no Vec inside the route).

Next: Trees with Box