RustArena

Step 3 of 6

Destructuring structs and tuples

Destructuring means taking a compound value apart into its pieces with a pattern. You've done it with tuples (let (a, b) = pair;). The same idea works for structs, and in every place a pattern can go: let, match arms, for loops and function parameters.

struct Point {
    x: i32,
    y: i32,
}

fn locate(p: &Point) -> String {
    match p {
        Point { x: 0, y: 0 } => String::from("at the origin"),
        Point { x: 0, y } => format!("on the y axis at {y}"),
        Point { x, y: 0 } => format!("on the x axis at {x}"),
        Point { x, y } => format!("at ({x}, {y})"),
    }
}

fn main() {
    let p = Point { x: 3, y: -7 };
    let Point { x, y } = p;
    println!("x = {x}, y = {y}");

    let Point { x: col, y: row } = p;
    println!("col {col}, row {row}");

    for q in [Point { x: 0, y: 0 }, Point { x: 0, y: 5 }, Point { x: 2, y: 0 }, p] {
        println!("{}", locate(&q));
    }
}
x = 3, y = -7
col 3, row -7
at the origin
on the y axis at 5
on the x axis at 2
at (3, -7)

Piece by piece:

Matching on several values at once

Put values in a tuple and match on the tuple. This replaces nested ifs when a decision depends on two things:

fn main() {
    for n in 9..=15 {
        let text = match (n % 3, n % 5) {
            (0, 0) => String::from("FizzBuzz"),
            (0, _) => String::from("Fizz"),
            (_, 0) => String::from("Buzz"),
            _ => n.to_string(),
        };
        print!("{text} ");
    }
    println!();
}
Fizz Buzz 11 Fizz 13 14 FizzBuzz

(0, _) means "first part is 0, second part is anything".

Ignoring fields with ..

.. means "and the rest, which I don't care about". It keeps patterns short when a struct has many fields:

let Order { id, total, .. } = order;      // ignore every other field
let (first, ..) = (1, 2, 3, 4);           // just the first element

Common mistakes

Your task

Finish this rock-paper-scissors referee. Each input line holds two moves (rock, paper or scissors). Match on the tuple (moves[0], moves[1]) and print player 1, player 2 or draw for each line. String literals work as patterns: ("rock", "scissors") => .... Rock beats scissors, scissors beat paper, paper beats rock.

Challenge 2

Write fn summary(order: &Order) -> String using struct patterns:

Use .. in at least one arm to skip the fields it doesn't need. Because you match on &Order, the bound fields are references; quantity * unit_cents still works on them.

Challenge 3

Write fn quadrant(point: (i32, i32)) -> &'static str from scratch. Match on the tuple:

Previous: Match guards and @ bindings Next: Nested enums and ignoring parts