RustArena

Step 1 of 6

Literals, ranges and |

In the enums module you used match to pick a branch for each variant. The left side of every arm (Some(x), None, _) is a pattern: a description of a shape a value might have. Rust compares the value with each pattern from top to bottom and runs the first arm that matches. Patterns can do much more than name variants, and this module covers every kind you'll meet at work.

Literals, alternatives and ranges

fn main() {
    for code in [200, 201, 301, 404, 418, 503] {
        let meaning = match code {
            200 | 201 => "success",
            300..=399 => "redirect",
            404 => "not found",
            400..=499 => "client error",
            500..=599 => "server error",
            _ => "unknown",
        };
        println!("{code}: {meaning}");
    }
}
200: success
201: success
301: redirect
404: not found
418: client error
503: server error

Piece by piece:

Ranges work on char too, which makes character classification short and readable:

fn kind(c: char) -> &'static str {
    match c {
        'a' | 'e' | 'i' | 'o' | 'u' => "vowel",
        'a'..='z' => "consonant",
        '0'..='9' => "digit",
        ' ' | '\t' | '\n' => "whitespace",
        _ => "other",
    }
}

fn main() {
    for c in ['e', 'k', '7', ' ', '!'] {
        println!("{:?}: {}", c, kind(c));
    }
}
'e': vowel
'k': consonant
'7': digit
' ': whitespace
'!': other

The return type &'static str is the type of a string literal: text stored inside the program itself, which lives as long as the program does (that's what 'static says; module 24 explains it fully). It's the usual return type for a function that picks one of several fixed labels.

The compiler checks every number

A match must be exhaustive: some arm has to match every possible value. For a number, that means every value of its type, and the compiler tells you exactly which ones you forgot:

fn main() {
    let n: u8 = 7;
    let size = match n {
        0 => "none",
        1..=9 => "a few",
    };
    println!("{size}");
}
error[E0004]: non-exhaustive patterns: `10_u8..=u8::MAX` not covered

Add _ => "lots" (or 10..=u8::MAX) to cover the rest. This check is why match is safer than a chain of ifs: when a new case appears, the compiler finds every place that must handle it.

Common mistakes

Your task

Fill in the blanks so level turns a battery percentage into a label: 0 is "empty", 1 to 20 is "low", 21 to 99 is "ok", 100 is "full", and anything above 100 is "invalid".

Challenge 2

A cinema charges by age. Write fn ticket_price(age: u32) -> u32 with a match and range patterns:

Age Price
0 to 12 5
13 to 17 8
18 to 64 12
65 and up 6

Challenge 3

Write a whole program that reads one line of text and counts its characters by kind, after turning the line into lowercase:

For the input Hello World 42! print:

vowels: 3
consonants: 7
digits: 2
spaces: 2
other: 1

Ignore the newline at the end of the line (use trim_end(), which removes whitespace at the end only).

Next: Match guards and @ bindings