Enums and Pattern Matching in Rust
Rust basics

The code for this article is available on GitHub.
Enum
An enum
is similar to struct
in the sense that it is a type for which you can have multiple variants (same as that of a class and its instance). But the variants of an enum
are fixed and defined within the enum
itself.
A Player
might be represented by a struct
:
struct Player {
name: String,
rank: i32,
}
You can instantiate multiple instances of a Player
. But let’s look at a type that is a little more niche.
enum PlayerAccountType {
Free,
Paid,
}
Imagine you have only two account types — a free and a paid one. You can use an enum
to represent this concept. And, since it just another type, you can use it in the Player
struct.
struct Player {
name: String,
rank: i32,
acc_type: PlayerAccountType,
}
You can represent the PlayerAccountType
type with a struct
, but you don’t have to since its possible variants are already known.
You can reference enum variants using ::
. For e.g., to instantiate a Player
who has a paid account:
let paid_member = Player{acc_type: PlayerAccountType::Paid, name: String::from("john"), email: String::from("john@doe.com")};
It’s also possible to add data to enum
variants. For e.g.:
enum foo {
foo1(String),
foo2(String),
}
Note:
- In addition to data,
enum
s can also have methods Option<T>
andResult<T,E>
are widely usedenum
s and are part of the Rust standard library.
Pattern Matching
Rust provides the match
keyword which behaves the same way as a switch
statement would (Rust does not have switch
). Before exploring match
, let’s see a simple if-else-if
example:
No surprises here! But you cannot write this using match
.
This is not possible because the match
operator needs a value against which it can execute the match process. It does not execute a given expression for you.
Update: As per this comment, if-else-if
is possible using Guards. Thanks, @rpring9!
The general format is as follows:
- You have a value you want to match against and a bunch of possible options, also known as a
match arm
. - Each arm is a combination of the pattern to match on and its corresponding
expression
to be run if the match is successful.
All this sounds abstract, so let’s look at an example of match
ing with an enum
.
Match enum
Let’s start by defining an enum
:
enum Choice {
One,
Two,
Three,
}
And match against Choice
s.
Here, the variable choice
is the value being match
ed, followed by three match
arms. For each arm, there is a pattern, e.g., Choice::One
and the corresponding expression, e.g., println!("Option 1")
separated by a =>
.
In this case, the output will be Option 1
.
Match an Option
The Rust standard library provides the Option
enum
whose purpose is to define a type to represent a scenario where you may or may not have a value.
This makes the code obvious and is a better choice than using null
, nil
, or similar options to denote the absence of a value
This is similar to Optional
in Java.
pub enum Option<T> {
Some(T),
None,
}
Don’t worry about the T
symbol. Just understand that it is a generic
type parameter to allow the enum
to work with various types rather than tying it to a specific one, e.g., a String
.
Let’s understand this with an example.
This is a simple CLI program that accepts an argument from the user (the name) and uses it to display a greeting.
If the user does not pass anything, it uses a default greeting. This is a reasonable example of how to use Option
since we may or may not have an argument passed in by the users (it’s optional! Duh!).
Here is the corresponding function:
The parse_name_arg
function converts passed-in arguments into a vector (Vec<String>
) and returns an Option
.
If an argument was not passed, it returns None
or Some
(with the value) — both are variants of an Option
. Now we can use this function as such:
let name = parse_name_arg();
match name {
Some(n) => println!("Hello {}!", n),
None => println!("Hello there!"),
}
We store the result of parse_name_arg
in a variable called name
(which is an Option
), match different possibilities, and execute the greeting accordingly.
If you were to pass john
as an argument, you will get back Hello john!
. If you don’t pass an argument, you will get back a generic greeting Hello there!
To try this out, just clone the GitHub repo, change to the correct directory, i.e., cd learning-rust/enums-match
, and use cargo run
. To pass an argument, you can use cargo run <your argument
.
The value of an enum is available in the match
clause (also called arm
). This is why we were able to extract the passed-in argument using Some(n) => println!("Hello {}!", n),
.
Using let with match
How about storing the greeting in a variable? You can use let
with match
to return a value as well. Let’s tweak the program a little bit.
let greeting = match name {
Some(n) => n,
None => String::from("there"),
};
println!("Hello {}!", greeting)
We stored the result of the match in a variable called greeting
and used it with the println!
macro which makes the program much simpler!
Exhaust Your Choices!
By default, match
requires you to fulfill or account for all the possible options. Let’s look at an example. Here is yet another enum
whose value we will match against.
And this is how we want to match (in this case, we happen to be interested in Friday
only):
let today = Days::Friday;
match today {
Days::Friday => println!("thank god its Friday!"),
}
But this will not compile. You will see an error as such:
error[E0004]: non-exhaustive patterns: `Monday`, `Tuesday`, `Wednesday` and 3 more not covered
To get around this problem, we can use the _
placeholder:
match today {
Days::Friday => println!("thank god its Friday!"),
_ => (),
}
This simply ignores the other possibilities. In such situations, you can also use if let
to keep it concise.
let today = Days::Monday;
if let today = Days::Monday {
println!("its Monday already! :(");
}
Conclusion
Don’t forget to check out these resources:
- The Rust Programming Language book
- Rust standard library documentation
- Rust by Example
That’s all for this quick tour of enum
s and how you can use pattern matching with them. Stay tuned for more!