RustArena

Step 2 of 7

Trees with Box

Lists have one "next" pointer per node. Trees have several: a node points at its children, and each child is a smaller tree. Box handles that the same way.

Expression trees

A calculator, a spreadsheet or a compiler stores a formula like (2 + 3) * 4 as a tree: the * node has two children, the + node and the number 4. Evaluating it is a short recursive function:

enum Expr {
    Num(i64),
    Add(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
}

use Expr::{Add, Mul, Num};

fn eval(e: &Expr) -> i64 {
    match e {
        Num(n) => *n,
        Add(a, b) => eval(a) + eval(b),
        Mul(a, b) => eval(a) * eval(b),
    }
}

fn main() {
    // (2 + 3) * 4
    let formula = Mul(
        Box::new(Add(Box::new(Num(2)), Box::new(Num(3)))),
        Box::new(Num(4)),
    );
    println!("{}", eval(&formula));
}
20

Building trees with Box::new everywhere gets noisy. Small helper functions like fn num(n: i64) -> Box<Expr> { Box::new(Num(n)) } make the code read like the formula.

Binary search trees with Option<Box>

When a child may be missing, use Option<Box<Node>>: None means "no child here". A binary search tree keeps smaller keys on the left and larger ones on the right, so walking it left-to-right visits the keys in sorted order:

struct Node {
    key: i32,
    left: Option<Box<Node>>,
    right: Option<Box<Node>>,
}

fn insert(tree: &mut Option<Box<Node>>, key: i32) {
    match tree {
        None => *tree = Some(Box::new(Node { key, left: None, right: None })),
        Some(node) => {
            if key < node.key {
                insert(&mut node.left, key);
            } else {
                insert(&mut node.right, key);
            }
        }
    }
}

fn in_order(tree: &Option<Box<Node>>, out: &mut Vec<i32>) {
    if let Some(node) = tree {
        in_order(&node.left, out);
        out.push(node.key);
        in_order(&node.right, out);
    }
}

fn main() {
    let mut root = None;
    for key in [50, 30, 70, 20, 40] {
        insert(&mut root, key);
    }
    let mut keys = Vec::new();
    in_order(&root, &mut keys);
    println!("{keys:?}");
}
[20, 30, 40, 50, 70]

Common mistakes

Your task

The calculator gained two operations: Sub(a, b) (a minus b) and Neg(e) (minus e). Write fn eval(e: &Expr) -> i64 for all five variants.

Challenge 2

Write fn show(e: &Expr) -> String that prints a formula with every operation in parentheses:

Challenge 3

Build a binary search tree of order ids. Using the Node struct below (smaller keys go left, equal or larger go right), write:

Previous: Box: a value on the heap Next: Deref and Drop: what makes a pointer smart