RustArena

Step 2 of 6

Match guards and @ bindings

A plain name in a pattern is a binding: it matches any value and gives it that name inside the arm. On its own that's not very selective, so you can add a match guard: if condition after the pattern. The arm only runs when the pattern matches and the condition is true.

fn describe(balance: i64, overdraft_limit: i64) -> String {
    match balance {
        0 => String::from("empty"),
        b if b < -overdraft_limit => format!("{b}: over the limit!"),
        b if b < 0 => format!("{b}: overdrawn"),
        b => format!("{b}: in credit"),
    }
}

fn main() {
    println!("{}", describe(0, 100));
    println!("{}", describe(-250, 100));
    println!("{}", describe(-40, 100));
    println!("{}", describe(900, 100));
}
empty
-250: over the limit!
-40: overdrawn
900: in credit

@ bindings: test a pattern and keep the value

name @ pattern checks the value against the pattern and binds it to name. It shines when the value is buried inside something, like a number inside Some:

fn main() {
    let readings = [Some(3), Some(42), Some(-1), Some(250), None];
    for r in readings {
        let text = match r {
            Some(n @ 0..=9) => format!("one digit: {n}"),
            Some(n @ 10..=99) => format!("two digits: {n}"),
            Some(n) if n < 0 => format!("sensor error {n}"),
            Some(n) => format!("large: {n}"),
            None => String::from("no reading"),
        };
        println!("{text}");
    }
}
one digit: 3
two digits: 42
sensor error -1
large: 250
no reading

Without @ you'd have to write Some(n) if n >= 0 && n <= 9, which is longer and easier to get wrong.

With |, a guard applies to all the alternatives: 1 | 2 if urgent => means "(1 or 2) and urgent".

Guards don't count for exhaustiveness

The compiler checks patterns, but it doesn't try to prove things about your guard conditions. To it, these two arms might both be false:

fn main() {
    let n: i32 = 7;
    let sign = match n {
        x if x < 0 => "negative",
        x if x >= 0 => "zero or positive",
    };
    println!("{sign}");
}
error[E0004]: non-exhaustive patterns: `i32::MIN..=i32::MAX` not covered

The fix is to make the last arm unguarded: _ => "zero or positive".

Common mistakes

Your task

Write fn speed_check(speed: u32, limit: u32) -> String using a match on speed with guards:

Challenge 2

Fill in the blank with an @ binding so the first arm catches 1 to 5 tickets left and names the number n.

Challenge 3

A smart thermostat reports Option<i32> (degrees Celsius, None when the sensor is offline). Write fn room_status(temp: Option<i32>) -> String:

Previous: Literals, ranges and | Next: Destructuring structs and tuples