Pattern Matching using Match Construct
Tags: rust
Published: 2023-05-01 11:41:40

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,
}
```

#### 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"),
    }
```

#### 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);

```

The above code produces below output

```
We got the value 1
Out of the match construct: x = Some(1), y = 2
```

#### 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"),
    }
```

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
```

#### 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"),
    }
```

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
``` 

#### 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})");
        }
    }
```

The above code produces below output

```
On the y axis at 10
```

#### 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}",)
        }
    }
```

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
```

#### 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 of `b` has an exact match ignoring whatever the values of `a`.

```
    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")
    } 
```

##### 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")
    }
```

#### 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")
    }
```

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](https://github.com/sivakumar-sivaprakasam/rust-examples/tree/main/pattern-match)

Please feel free to share your comments if any

Happy reading!!!