Weekly Rust Trivia is a problem-oriented series of articles that assist developers while learning Rust. Every article solves simple, everyday development tasks using the Rust standard library or leveraging popular and proven crates.

Question: How to use pattern matching in Rust?

In Rust, pattern matching is a powerful language feature that allows you to destructure data and match it against specific patterns. It helps you handle different cases and extract values from complex data structures with concise and readable code. The match keyword is used to perform pattern matching. Inside a match block, you define patterns and corresponding code blocks for each pattern. When the input data matches a specific pattern, the corresponding code block will be executed.

For demonstration purposes, let’s consider having the following enum to deal with weather conditions:

enum Weather {
    Sunny,
    Cloudy,
    // the u32 represents the amount of rainfall in millimeters
    Rainy(u32), 
    Snowy { snow_depth: u32 },
}

With the Weather enum in place, we can use pattern matching to control the flow of our application:

fn main() {
    let today_weather = Weather::Rainy(25);
    let mut message;
    match today_weather {
        Weather::Sunny => println!("It's a sunny day!"),
        Weather::Cloudy => println!("It's a cloudy day."),
        Weather::Rainy(amount) => println!("It's a rainy day. Rainfall: {}mm", amount),
        Weather::Snowy {
            snow_depth,
        } => {
            println!("It's a snowy day.");
            println!("Snow Depth: {}cm", snow_depth);
        }
    }
}

We can optimize the previous code and assign the actual message to a local variable. To do so, every “arm” must return a value of the same type (String here):

fn main() {
    let today_weather = Weather::Rainy(25);
    
    let message = match today_weather {
        Weather::Sunny => String::from("It's a sunny day!"),
        Weather::Cloudy => String::from("It's a cloudy day."),
        Weather::Rainy(amount) => format!("It's a rainy day. Rainfall: {}mm", amount),
        Weather::Snowy {snow_depth} => format!("It's a snowy day.\nSnow Depth: {}cm", snow_depth),
    };
    println!("{}", message);
}

Pattern matching, also comes with a wildcard pattern. The wildcard pattern (_) in Rust’s pattern matching acts as a default case, handling unmatched situations. It allows us to ignore specific values or variants, ensuring exhaustiveness and providing concise, robust code. Again let’s take the Weather enum from the previous sample and implement pattern matching using the wildcard pattern:

fn main() {
    let today_weather = Weather::Rainy(25);
    
    let message = match today_weather {
        Weather::Sunny => String::from("It's a sunny day!"),
        Weather::Cloudy => String::from("It's a cloudy day."),
        _ => String::from("Weather is not that good today.")
    };
    println!("{}", message);
}

Although pattern matching is easy to grasp and use, it is a powerful language feature that you can use to streamline your code, make it more readable, and increase maintainability.