Pattern Matching using Match Construct

ssivakumar

Sivakumar

Posted on May 1, 2023

Pattern Matching using Match Construct

In this blog post, let us see one of a powerful feature of Rust match. Rust has an extremely powerful control flow construct called match that allows you to compare a value against a series of patterns and then execute code based on which pattern matches. Patterns can be made up of literal values, variable names, wildcards, and many other things. The power of match comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled.

Syntax

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
Enter fullscreen mode Exit fullscreen mode

Matching Literals

match construct can be used to check literals.

    let x = 10;

    match x {
        10 => println!("Ten"),
        20 => println!("Twenty"),
        30 => println!("Thirty"),
        _ => println!("Others"),
    }
Enter fullscreen mode Exit fullscreen mode

Matching Named Variables

match construct starts a new scope variables declared as part of a pattern inside the match expression will shadow those with the same name outside the match construct, as is the case with all variables. In the below example, we declare a variable named a with the value Some(1) and a variable b with the value 2. We then create a match expression on the value a.

    let x = Some(1);
    let y = 2;

    match x {
        Some(10) => println!("We got the value 10"),
        Some(y) => println!("Matched, y = {y}"),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("Out of the match construct: x = {:?}, y = {y}", x);

Enter fullscreen mode Exit fullscreen mode

The above code produces below output

We got the value 1
Out of the match construct: x = Some(1), y = 2
Enter fullscreen mode Exit fullscreen mode

Matching Multiple Patterns

match construct supports matching multiple patterns using the | syntax, which is the pattern or operator. Lets check this below example

    let a = 10;

    match a {
        10 | 20 => println!("Ten or Twenty"),
        3 => println!("Thirty"),
        _ => println!("Not in the range"),
    }
Enter fullscreen mode Exit fullscreen mode

In the above code we match the value of a against the match arms, the first of which has an or option, meaning if the value of a matches either of the values in that arm. The above code produces below output

Ten or Twenty
Enter fullscreen mode Exit fullscreen mode

Matching Ranges of Values with ..=

match construct supports the ..= syntax which allows us to match to an inclusive range of values.

    let a = 10;

    match a {
        1..=9 => println!("Between one to nine"),
        10..=20 => println!("Between ten to twenty"),
        _ => println!("Not in the range"),
    }
Enter fullscreen mode Exit fullscreen mode

In the above code, when a pattern matches any of the values within the given range, that arm will execute. The above code produces below output

Between ten to twenty
Enter fullscreen mode Exit fullscreen mode

Matching Structs

match construct also works with struct. In the below code, we have a Point struct with two fields named x and y. When the match arm matches any or all of the values, the corresponding arm will get executed. In the below example, we've a point struct with field values x=0 and y=10 hence the 2nd match arm Point { x: 0, y } gets executed.

    let p = Point { x: 0, y: 10 };

    match p {
        Point { x, y: 0 } => println!("On the x axis at {x}"),
        Point { x: 0, y } => println!("On the y axis at {y}"),
        Point { x, y } => {
            println!("On neither axis: ({x}, {y})");
        }
    }
Enter fullscreen mode Exit fullscreen mode

The above code produces below output

On the y axis at 10
Enter fullscreen mode Exit fullscreen mode

Matching Enums

match construct also works with enum like struct. We can deconstruct enum using match construct. Let us see an example below

    enum Message {
        Exit,
        Point { x: i32, y: i32 },
        Print(String),
       RGB (i32, i32, i32),
    }

    let m = Message::Print(String::from("Hello World"));
    match m {
        Message::Exit => {
            println!("Exit Enum");
        }
        Message::Point { x, y } => {
            println!("Point with x axis {x} and y axis {y}");
        }
        Message::Print(text) => {
            println!("Printing message: {text}");
        }
        Message::RGB(r, g, b) => {
            println!("Red {r}, Green {g}, and Blue {b}",)
        }
    }
Enter fullscreen mode Exit fullscreen mode

In the above code, we have Message enum with Print variant. When we deconstruct this enum using match, the corresponding arm gets executed and produces below output

Printing message: Hello World
Enter fullscreen mode Exit fullscreen mode

Ignoring Values in Match Pattern

We've seen that it’s sometimes useful to ignore values in a pattern, such as in the last arm of a match, to get a catchall that doesn’t actually do anything but does account for all remaining possible values. There are a few ways to ignore entire values or parts of values in a pattern: using the _ pattern, using the _ pattern within another pattern, using a name that starts with an underscore, or using .. to ignore remaining parts of a value. Let’s explore how and why to use each of these patterns.

Ignoring an Entire Value with _

It is possible to ignore entire value using _. In the below example, we have two variables a and b. We're checking if the values ofbhas an exact match ignoring whatever the values ofa`.

`
let a = 5;
let b = 10;

match (a, b) {
    (_, 10) => println!("Ignored a but matched b which is having value 10"),
    _ => println!("All othe cases")
} 
Enter fullscreen mode Exit fullscreen mode

`

Ignoring Remaining Parts of a Value with ..

We can use .. to ignore remaining parts of value in the match.

`
let x = 1;
let y = 2;
let z = 3;

match (x, y, z) {
    (1, ..) => println!("x is 1 but ignored the other values"),
    _ => println!("All other cases")
}
Enter fullscreen mode Exit fullscreen mode

`

Extra Conditionals with Match Guards

A match guard is an additional if condition, specified after the pattern in a match arm, that must also match for that arm to be chosen. Match guards are useful for expressing more complex ideas than a pattern alone allows.

`
let number = 50;

match number {
    x if x % 10 == 0 => println!("{x} is divisable by 10"),
    x if x % 3 == 0 => println!("{x} is divisable by 3"),
    _ => println!("All other cases")
}
Enter fullscreen mode Exit fullscreen mode

`

In the above code example, we've match arm with extra condition to test more specific case.

Hope this blog post gives you detailed insight of enum construct. You can get the code samples from this link

Please feel free to share your comments if any

Happy reading!!!

💖 💪 🙅 🚩
ssivakumar
Sivakumar

Posted on May 1, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related