Pybites Logo Rust Platform

Advanced Pattern Matching

Medium +3 pts

🎯 Python 3.10 introduced structural pattern matching:

match command:
    case {"action": "move", "x": x, "y": y}:
        print(f"Moving to {x}, {y}")
    case {"action": "quit"}:
        print("Goodbye")

Rust's match goes further with features you'll use constantly: ranges, multiple patterns, guards, destructuring, and exhaustiveness checking. This exercise drills all of them.

Ranges and multiple patterns

match score {
    90..=100 => "A",                // range pattern
    75..=89 | 70..=74 => "B/C",    // multiple patterns with |
    _ => "below",                   // catch-all
}

Python's match doesn't support ranges directly — you'd use case x if 90 <= x <= 100. Rust makes it first-class syntax.

Guards

Guards add conditions to patterns:

match age {
    n if n >= 18 => "adult",
    13..=17 => "teen",
    _ => "child",
}

Python has guards too (case x if x >= 18), so this concept transfers directly.

Destructuring

Rust can destructure tuples, structs, and enums in match arms:

match (width, height) {
    (w, h) if w == h => w * w,       // destructure tuple + guard
    (w, h) => w * h,
}

Enum destructuring

Enums with data are destructured to extract their contents:

enum Command {
    Say(String),
    Resize { w: u32, h: u32 },
    Exit,
}

match cmd {
    Command::Say(s) if !s.is_empty() => s,
    Command::Say(_) => "(nothing)".into(),
    Command::Resize { w, h } => format!("{w}x{h}"),
    Command::Exit => "done".into(),
}

Each variant can have its own guard. The compiler ensures all variants are handled.


Your Task

First complete classify_char(c: char) -> &'static str classifying a character:

  • '0'..='9'"digit"
  • 'a'..='z' | 'A'..='Z' | '_'"alpha"
  • '+' | '-' | '*' | '/' | '='"op"
  • ' ' | '\t' | '\n'"ws"
  • Anything else → "other"

Then implement sum_positive_pairs(input: &[(i32, i32)]) -> i32 summing x + y only for pairs where both values are positive, using tuple destructuring with a guard.

Lastly, use the defined Message enum and implement route(msg: Message) -> String:

  • Ping"pong"
  • Echo(s) → return s if non-empty, else "silence" (use a guard)
  • Move { x, y }"noop" if both zero, else "move:{x},{y}"
  • Quit"bye"

Example

assert_eq!(classify_char('8'), "digit");
assert_eq!(classify_char('_'), "alpha");
assert_eq!(classify_char('+'), "op");

assert_eq!(sum_positive_pairs(&[(1, 2), (-1, 3), (4, 0)]), 3);

use Message::*;
assert_eq!(route(Ping), "pong");
assert_eq!(route(Echo("hi".into())), "hi");
assert_eq!(route(Echo("".into())), "silence");
assert_eq!(route(Move { x: 0, y: 0 }), "noop");
assert_eq!(route(Move { x: 2, y: -1 }), "move:2,-1");

Dive deeper: In our Rust Developer Cohort, pattern matching is the heart of your JSON parser — matching on token variants, destructuring String(s) and Number(n), and using guards to handle edge cases.


Further Reading