Match: Switch on Steroids

Welcome to Day 17! By now, we’ve covered some seriously cool Rust features, but today might be my favorite so far: the match expression. If you’re coming from a C# background—especially with the newer C# 8+ switch expressions—you might think you’ve seen it all. Trust me, Rust’s match is like a switch statement on steroids, protein shakes, and a Red Bull chaser.

Basic Match: Rust’s Superpowered Switch

In Rust, match is incredibly powerful and expressive. At its core, it’s used to match a value against multiple patterns. Think of it like a Swiss Army knife—it can handle anything from simple cases to complex scenarios with ease:

fn main() {
    let number = 4;

    match number {
        1 => println!("One!"),
        2 | 3 | 5 | 7 => println!("Prime"),
        4..=10 => println!("Between 4 and 10"),
        _ => println!("Something else"),
    }
}

Cool, right? Rust lets you handle multiple values (2 | 3 | 5 | 7) and even ranges (4..=10) with clean, concise syntax.

Pattern Matching vs. C# Switch Expressions

C# devs might recognize pattern matching from recent language updates. Let’s quickly remind ourselves of what C# 8+ switch expressions look like:

var number = 4;

var result = number switch
{
    1 => "One",
    2 or 3 or 5 or 7 => "Prime",
    >= 4 and <= 10 => "Between 4 and 10",
    _ => "Something else"
};

Console.WriteLine(result);

Both languages share similarities, but Rust’s match goes even further:

  • Exhaustive Checks: Rust enforces exhaustive pattern matching at compile-time. If you miss a case, Rust won’t compile until you handle every possible scenario.

  • Powerful Patterns: Rust can match complex data types, destructure tuples and structs, and even match on enum variants.

Advanced Match: Pattern Matching on Complex Types

Here’s where Rust really flexes its muscles:

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

enum UsState {
    Alabama,
    Alaska,
    // more states...
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        },
    }
}

fn main() {
    let coin = Coin::Quarter(UsState::Alaska);
    println!("Value: {} cents", value_in_cents(coin));
}

This example demonstrates how match elegantly destructures and matches data within enums—something C# is starting to do but not yet as fluidly.

The Wildcard _ to the Rescue

One more powerful feature: Rust’s wildcard _. This ensures every case is covered without explicitly defining them all:

let some_number = Some(5);
match some_number {
    Some(7) => println!("Lucky number 7!"),
    _ => println!("Some other number"),
}

Why Rust’s Match Is a Game-Changer

  • Safety: You simply cannot miss a case by accident. Rust forces you to handle every scenario explicitly or use a wildcard.

  • Readability: Complex logic stays concise and clear.

  • Maintainability: Adding or modifying patterns is straightforward, making your code easy to evolve and understand.

Wrap-Up

Rust’s match isn’t just powerful—it’s addictive. Once you start using it, you’ll wonder how you ever managed complex branching logic without it. It’s more than just a switch statement; it’s pattern matching at its very best.

Tomorrow, we’ll push pattern matching even further with destructuring—prepare for another thrilling Rust adventure!

Work with me

Need this kind of thinking on your team?

Fractional architecture, a decision brief, or a workshop built around the problems in these articles.

Get in touch