RustArena

Step 4 of 6

Nested enums and ignoring parts

Patterns nest exactly as deep as your data does. An enum variant can hold a struct-like set of fields, and those fields can hold other enums; one pattern can look at all of it.

enum Button {
    Left,
    Right,
}

enum Event {
    Key(char),
    Click { x: i32, y: i32, button: Button },
    Scroll(i32),
    Quit,
}

fn handle(event: &Event) -> String {
    match event {
        Event::Key('q') | Event::Quit => String::from("quit"),
        Event::Key(c @ '0'..='9') => format!("digit {c}"),
        Event::Key(c) => format!("key {c}"),
        Event::Click { button: Button::Right, .. } => String::from("open menu"),
        Event::Click { x, y, button: Button::Left } => format!("left click at ({x}, {y})"),
        Event::Scroll(0) => String::from("no scroll"),
        Event::Scroll(n) if *n > 0 => format!("scroll down {n}"),
        Event::Scroll(n) => format!("scroll up {}", -n),
    }
}

fn main() {
    let events = [
        Event::Key('7'),
        Event::Key('w'),
        Event::Click { x: 10, y: 20, button: Button::Left },
        Event::Click { x: 5, y: 5, button: Button::Right },
        Event::Scroll(-3),
        Event::Scroll(4),
        Event::Key('q'),
        Event::Quit,
    ];
    for e in &events {
        println!("{}", handle(e));
    }
}
digit 7
key w
left click at (10, 20)
open menu
scroll up 3
scroll down 4
quit
quit

Piece by piece:

_, _name and ..

Pattern Meaning
_ match one value, don't bind it (nothing is moved or borrowed)
_name bind the value (it is moved or borrowed) but don't warn if it's unused
.. skip all remaining fields or elements

Common mistake: moving out while matching

Matching on an owned value with a binding moves the data out of it:

fn main() {
    let name: Option<String> = Some(String::from("Ferris"));
    match name {
        Some(n) => println!("hi {n}"),
        None => println!("nobody"),
    }
    println!("{:?}", name);
}
error[E0382]: borrow of partially moved value: `name`

Some(n) moved the String into n, so name is no longer whole. Match on a reference instead (match &name), and n becomes a &String. If you don't need the value at all, Some(_) doesn't move anything.

Your task

A checkout supports three payment methods. Write fn total_charge(p: &Payment) -> u32, the amount charged in cents including the card fee:

Challenge 2

Fill in the three blanks: match the nested Status::Active, bind the days field of Suspended, and ignore the other fields of a closed account.

Challenge 3

A warehouse robot gets commands. Write fn command_label(cmd: &Command) -> String:

Previous: Destructuring structs and tuples Next: while let and slice patterns