Skip to Content
Course content

25: Pattern Matching with match and if let

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this pattern a dozen times in code reviews from developers moving to Rust from Java or Python. They treat Option or Result types like they're just nullable objects, leading to code that feels clunky and fights the compiler.

// A realistic mistake: The "Check-then-Unwrap" pattern
fn print_user_status(user_id: u32, users: &HashMap<u32, User>) {
    if users.contains_key(&user_id) {
        let user = users.get(&user_id).unwrap(); 
        println!("User {} is active: {}", user.name, user.is_active);
    } else {
        println!("User not found.");
    }
}

The Double-Lookup Tax

At first glance, the code above works. It doesn't crash, and it handles the "not found" case. But it's inefficient. You're asking the HashMap to find the key twice: once for contains_key and once for get. In a tight loop, this is a waste of cycles. More importantly, calling .unwrap() is a habit that eventually leads to a panic in production when you forget a check somewhere. You're essentially telling the compiler, "Trust me, I already checked this," which defeats the purpose of Rust's safety guarantees.

Solving for Exhaustiveness with match

The "Rust way" is to embrace the fact that get() returns an Option. Instead of checking if the value exists and then grabbing it, we do both in one motion using match. This forces us to handle every possible outcome—what we call "exhaustive matching."

fn print_user_status(user_id: u32, users: &HashMap<u32, User>) {
    match users.get(&user_id) {
        Some(user) => println!("User {} is active: {}", user.name, user.is_active),
        None => println!("User not found."),
    }
}

Notice how much cleaner this is. We've collapsed the check and the retrieval into a single operation. If I were to add a new variant to the return type (though Option is fixed), the compiler would scream at me until I handled it. That's the safety net I rely on every day.

Trimming the Noise with if let

Now, match is powerful, but it can feel like overkill. Imagine you're writing a function where you only care if the user exists, and if they don't, you just want to silently return or do nothing. Writing None => () feels like boilerplate noise.

That's where if let comes in. It's essentially syntactic sugar for a match that only cares about one specific pattern.

fn notify_admin(user_id: u32, users: &HashMap<u32, User>) {
    // We only care about the Some case; the None case is implicitly ignored
    if let Some(user) = users.get(&user_id) {
        send_alert(&user.email, "A user has logged in.");
    }
}

I usually follow a simple rule of thumb: use match when you have two or more distinct paths of logic that must be handled. Use if let when you're essentially saying, "If this is the case, do this thing; otherwise, just keep moving." It keeps the indentation shallow and the intent clear.




📋 Practical Task

Implementing a Game Command Processor

You are building a simple text-adventure game. You have an enum representing possible player commands, and some of those commands carry additional data (like the item name the player wants to pick up).

Your Task: Create a function process_command that takes a Command enum. Use a match statement to handle the following logic:

  • Command::Move(direction): Print "Moving to the [direction]!".
  • Command::PickUp(item): Print "You picked up the [item]!".
  • Command::Quit: Print "Goodbye!".
  • Command::Wait: Print "You wait patiently...".

Additionally, write a separate snippet using if let that specifically checks if the command is PickUp. If it is, print "Inventory updated!", otherwise do nothing.

enum Command {
    Move(String),
    PickUp(String),
    Quit,
    Wait,
}

fn process_command(cmd: Command) {
    // Your match implementation here
}

fn main() {
    let my_cmd = Command::PickUp(String::from("Rusty Sword"));
    process_command(my_cmd); 
    
    // Your if let implementation here
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.