- In the previous section,
we saw how to use an if statement in Rust,
and now we're going to look at the match keyword.
So, match is kind of similar to switch in other languages,
but a bit more powerful maybe
than what you've seen in those other languages.
You can use match to test against integers,
and Booleans, and enums.
Imagine you had an enum for color, for example, red, green,
or blue.
You could have a match statement that says, you know,
is it red?
Is it green?
Is it blue?
So, a bit like a switch statement, but more powerful.
You can also use match in Rust to match arrays,
which is surprising, and tuples.
A tuple we'll have a look at later.
A tuple is kind of like an array,
but where the element types can be different.
So, a tuple could have like an integer,
a string and a float for example.
And also, structures.
Structures are a bit like classes
in object-oriented languages.
So, you can use a match keyword to match any of these types.
We'll have a look at all of these
types later on in the course,
but for now we'll just focus on integers
because we just want to look at the basic syntax
for match itself before we get carried away.
So, here's the basic idea.
I've got an integer, num,
and then you see match
and then you give it the value that you want to test on.
So, I'm basically testing that's
the expression that I'm testing,
and then inside curly brackets you have each
of the possible outcomes.
It's like a switch case, but different syntax.
In the case where the number is 100,
then it will execute this code.
The arrow is formal syntax.
This is called an arm in Rust.
In switch you'd call it a branch,
but in Rust you call it an arm like in a crab,
and then you have a comma between arms.
Okay, so if the number was 100
it would print 100.
If the value of num was 200 it'll go in there.
You have to have a catch all,
the underscore catches any other value.
So, if it was anything else it would come in here.
Okay, so some points that I need to make.
First of all, these branches are called arms.
So, I should say this is one arm, that's another arm,
that's another arm.
You can enclose arms in curly brackets.
If you have more than one statement to execute in an arm,
then you have to group them together using curly brackets.
So, I could do that.
If the number was 100, I could say curly bracket,
do some processing here ....,
close curly bracket comma.
So, you have a comma between the arms and curly brackets
to group them together if you need to.
The arms must be exhaustive,
so Rust will complain if you don't cater
for every possible value.
When you declare a number,
it could be any number between kind of minus 32,000
and plus 32,000.
So, if I didn't have this catch-all,
the underscore at the end,
I'd get an error from Rust
because maybe the number wasn't captured by this arm,
or that arm and it needs to do something
in each match statement.
Okay, so typically you will have a default.
This is equivalent to default in other languages
to make sure that you're catching all possible outcomes.
Okay, so you can match ranges as well as individual numbers.
So here I'm matching the number in a range,
25.. equals 50.
So, basically, from 25 up to and including 50.
So, from a syntax point of view,
it's two dots here, not three.
You might have seen in other languages.
And the upper limit, you have to use this syntax
for the upper limit to be inclusive.
Okay, the upper limit must be inclusive.
It's a language limitation at the moment.
So here, if the number was 25 through to 50,
it would come into the first branch.
If the number was 51 to 100, then it would come
into the second branch.
If it was anything else,
then it goes into the default branch
and it does that default processing.
Okay, next up you can match multiple patterns.
You can use the kind of like the pipe symbol
to represent kind of like an or condition.
So, I'm testing here if the number,
if num is 25, or 50, or 75,
then basically go into that first branch like so.
On the other hand, if the number is 100, or 200,
then go into this branch.
And then if it's something else,
then go into the default branch at the end.
Okay, so that's quite a nice kind of technique.
This is the equivalent concept of like a fall through
in other languages, we'd say,
switch the value 25 colon, case 50 colon, case 75 colon.
This is how you achieve the same effect
of fall through in Rust using the,
it's like an OR operator, like a pipe symbol.
You can also put conditions.
You can have a matching condition at the end here.
So, this is really starting to look quite powerful.
I've taken the number and it kind of...
what it will do is it'll put...
it'll kind of copy the number into this variable, x,
and then it has some kind of if statement, okay.
So, copying the number into x, and then testing,
is x less than 50?
So basically, is the number less than 50?
If it is, then this variable, you can use it over here.
I could have actually used x.
It kind of extracts the number into x
and that variable then exists.
You can use it here if you wanted to.
And so that's the first condition.
The second branch or the second arm,
if the number is exactly equal to 75,
then basically extract it into X,
so I could use X in here,
and then I've got a catch-all at the end.
So, you can have if statements in your arms
which is quite interesting.
So, these are called match guards.
It'll only match if the guard is satisfied.
Right then, have a look at this example.
Similar to what we had before, but in this example,
basically I'm using match as an expression.
So, it'll evaluate to be a single result.
And the result of the whole thing is then assigned
into that variable there.
So, I can use match as an expression as opposed
to just a statement,
which is what I had in the previous examples.
So, let's evaluate,
and by the way you have to have a statement semicolon
at the end to, you know, separate it from other statements.
So, let's have a look at this expression to see
which value it yields.
So, it comes in, it checks the number,
basically if the number is less than 50
then in that case this is the outcome of the whole thing.
The whole match expression will evaluate
to be either the result of one arm,
or another arm or another arm like that.
Okay, so one of those three values will be the result
of the whole thing either that value will be assigned
into res,
or this value
will be assigned into res,
or this value will be signed into res.
Okay, so one of those three arms will be evaluated,
and the value in that arm will be assigned
into the variable,
okay, which you can then output.
So, the motivation for using an expression format
is that you avoid duplication.
All you're interested in is like
getting out the value into res,
and then you can use res here
in one consistent statement afterwards.
Okay, example time,
so we'll go into the demo project lesson 3 flow control
this time it's the demo match function
we're gonna look at,
so and then we'll run it obviously let's have a look.
So, as before this is our lesson flow control.
I'm gonna open that project in Visual Studio Code.
Here we are and go into the main code.
In my main function, I'm going to uncomment the demo match
and here is the code for demo match.
Kind of what we had in the PowerPoint slide,
but I'm gonna do some experimentation just
to show the kind of errors
that you'll get if you don't do things exactly right.
So, let's open up a terminal window,
and I'll run the application in its current format.
Everything should be good here.
So, cargo run, just give that a while to finish.
The usual warnings about unused functions
and then the output.
So first of all, num is 100.
When it does the match here it'll come into 100
and it prints 100, which is great, fantastic.
And then it matches number is 100,
so it's gonna come into this branch, this arm here,
and it says 51 to 100, so that's good 51 to 100.
And then moving on remember the number is 100,
so when I have this kind of a multi-value match
it's going to match in this case label here this arm
is going to be executed,
so it'll give me a 100 to 200 that's good,
and then now, my number isn't less than 50,
it's not 75,
so it must be something else.
So, it prints out something else.
A little bit vague, but you get the drift.
And then finally, this is my expression, res will be,
each of these branches will be evaluated until it matches.
And the result of each branch or arm,
whichever arm it goes into,
the result will be assigned into res.
It's going to be this arm here, isn't it?
It's not 50, it's not less than 50,
it's not 75, it's something else.
That string will be assigned into res,
and then I can output the result of the magic expression
is something else.
Okay, so that all kind of worked as we expected.
Let's see some errors.
Let's see what happens, for example,
if I accidentally forget to be exhaustive.
Okay, so I'll reset my number to be 99,
just to prove the point
that it doesn't necessarily need to be 100 or 200.
And let's see what happens
when I have that first match statement.
So, cargo run.
Okay, I am getting an error on line,
it's complaining about line 46.
Yes, line 46.
My number is an integer which can go from like, well,
minus a big number to plus a big number,
and it's saying the patterns that are specified,
these are called patterns,
it only caters for, it doesn't cater for,
like minus the smallest number up to 99.
The minimum value,
it tells me that this is an I32 by the way,
I32 has a constant called min.
So, from the minimum value up to 99,
and then from 101 up to 199 and then from 201 up
to the maximum number for an i32.
Basically, everything apart from 100
and 200 are not catered for in my conditions.
It's not exhaustive and it has to be exhaustive.
So, I would need to add in the catch all here
to make it exhaustive, okay.
And then one other thing I'll just point out
before we kind of close this demo.
If you're doing a match condition,
like a range I should say,
the range here has to be inclusive.
You've got to say, you know, up to and include in 50.
If you leave off the equal sign,
like you might be tempted to do,
let me just make sure I've saved that file,
then watch what happens now.
It says I've got an error on line 53.
Okay, so here, I've tried to have,
what I've tried to do is have an exclusive upper bound
I've tried to basically say 25 to 49 effectively,
but it says this syntax here the exclusive range pattern
is experimental in other words you're not allowed
to have an exclusive pattern like up to,
but not including.
It has to be inclusive to include the upper limit like so.
Okay, so if you really only wanted to include up to 49
then you would have to write it that way.
And if you really just wanted to include it up to,
but not including 100,
you'd have to write it that way up to including 99,
and that would then give you the effect
that you're looking for.
Okay, so that's the match keyword.
We've just been using match here with simple integers.
All ready, you can kind of see that it's quite powerful
with range matching and multiple values matching
and having guards like an if statement as part
of your condition and being able to evaluate match
as an expression to assign to a variable.
So, it's quite powerful already.
What we'll do later in the course is have a look
at how to use match with other types like enums,
and arrays, and tuples, and structures.