Learning Rust - Understanding pattern matching
Tags: rust, functional, vectors
Published: 2020-01-03 20:18:19

**Introduction**

Rust favors immutability by default, which can lead to somewhat unexpected behavior when coming from other languages.
We will see how pattern matching can be combined with immutability, and why it can be a powerful tool to have in our toolbox when dealing with code that we want to perform specific actions when encountering certain inputs.

**Recap of let**

Previously, we saw that `let` could be used to declare variables in Rust, and, when followed by the `mut` keyword, it would declare a mutable variable, whose value can be re-assigned during runtime, as opposed to an immutable variable which can't change. The syntax is as follows:

```rust
let x = 4;
let mut y = 1;
```

So, in the above snipped, we declare and initialize two variables that hold integer values, `x` and `y`.

The main difference between the two, is that `y` is mutable, whereas `x` is not. As a consequence, if we try to increment `y`, it all works perfectly:

```rust
use std::env;

fn main() {
    let x = 4;
    let mut y = 1;
    println!("Before update {}",y);
    y=y+1;
    println!("After update {}",y);
}
```

This will work as we want and output:

```
Before update 1
After update 2
```

However, when we try to do the same, but, for `x` which is immutable, here's what we will get:

```rust
error[E0384]: cannot assign twice to immutable variable `x`
 --> pattern_match.rs:7:5
  |
4 |     let x = 4;
  |         -
  |         |
  |         first assignment to `x`
  |         help: make this binding mutable: `mut x`
...
7 |     x=x+1;
  |     ^^^^^ cannot assign twice to immutable variable
```

The first line, describing the error is really clear: `error[E0384]: cannot assign twice to immutable variable x `: what this means is that we are attempting to re-assign a value to an immutable variable, which is not allowed.

So, we see that the way to use let, in case of both mutable and immutable bindings, is the same, but, the mutability or absence of it, can affect what we can do with the bindings during run-time.

**Pattern matching**

We can do more than simple value assignment with let, and, when dealing with a complex expression, we can use a pattern against which to match the expression to execute a certain action. Quoting the Rust book:

Patterns are a special syntax in Rust for matching against the structure of types, both complex and simple. Using patterns in conjunction with match expressions and other constructs gives you more control over a program’s control flow. A pattern consists of some combination of the following:

- Literals
- Destructured arrays, enums, structs, or tuples
- Variables
- Wildcards
- Placeholders
These components describe the shape of the data we’re working with, which we then match against values to determine whether our program has the correct data to continue running a particular piece of code.

To use a pattern, we compare it to some value. If the pattern matches the value, we use the value parts in our code.

We still do not know what all of these things are, but, we can already use what we know to introduce the syntax and idea of pattern matching.

The idea of pattern matching is as follows:

*A pattern consists on a combination of components that describe the data we are working with, and, we can compare it against some value, and, when the pattern matches a certain value, we execute some action. For values that do not match the pattern, no action will be executed*

Let's assume we want to print an informative message, that, if a certain value is 10, it means that a tram will arrive on line 10, and, if it is different, we assume that the tram will not be on line 10. 

```rust
let tram_schedule = 10; //Tram is on line 10!!
match tram_schedule {
    10 => println!("Tram arriving to line 10"),
    _ => println!("No tram on line 10"),
}
```

If we run the above code, we will see the message `Tram arriving to line 10`

Let's go over the syntax:

We have the definition of a `match` expression above. From the Rust docs:

Formally, match expressions are defined as the keyword match, a value to match on, and one or more match arms that consist of a pattern and an expression to run if the value matches that arm’s pattern, like this:

```
match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
```

One requirement for match expressions is that they need to be exhaustive in the sense that all possibilities for the value in the match expression must be accounted for. One way to ensure you’ve covered every possibility is to have a catchall pattern for the last arm: for example, a variable name matching any value can never fail and thus covers every remaining case.

A particular pattern _ will match anything, but it never binds to a variable, so it’s often used in the last match arm. The _ pattern can be useful when you want to ignore any value not specified, for example.

We can also match against a range of values. Let's assume now that we are waiting for a tram, and, if the value of our waiting time is between 1 and 10, we assume to be on time, if else, it is delayed. 

Here's how we can match against **an inclusive range** of values:

```rust
let time = 10;

match time {
    1..=10 => println!("Tram on time"),
    _ => println!("Delayed"),
}
```

The special syntax, `..=` can be used to match a pattern against a range of values.

We can also use the pipe operator `|` serving as an or, matching one value or the other, like in:

```rust
let x = 1;

match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}
```

The above code will print `one or two`.

**Conclusion**

This was a short introduction to pattern matching and how it is useful to control execution flow of our Rust programs.
We saw how it can be used to handle more complex cases like matching sets or even ranges of values, and how it can be more versatile than simpler if-else constructs.

The mandatory pattern that needs to be always present to cover any non-matched pattern makes the code more robust and safer as well!