Hello my dear readers,
Today we're going to fly through enums
and have a think about where enums
are a better design choice over structs
. We'll also take a look at a shorthand match
expression known as an if let
expression.
Yesterday's questions answered
- Rust will auto deref as much as is required. It will add a maximum of one reference to the result.
Today's open questions
- Nothing open today...
Data and behaviour under one type
In yesterday's post, we saw how structs
can be used to group data into fields
. Operations on these fields
(methods) are then often defined in impl
blocks for that struct
. We also learned that a struct
is also a type. This means that if we want to use functions or methods on different structs
declared with very similar fields
or methods, we'll need a generic implementation to handle the static typing.
Another alternative is to group these structs as variants
of an enum
. This is how the Rust std
library implements an enum
representing different kinds of IP addresses. enums
are very flexible in Rust, an enum variant
can be of virtually any type. In comparison with Python, enums
are central to implementations of key concepts such as Option
and Result
types.
Matching enums is easy
Part of the reason why enums
are baked into the std
library and generally useful in Rust design patterns is because of match
expressions. enum variants
applied in the left side of a match arm often provide for readable and expressive code:
use rand::Rng;
use std::cmp::Ordering;
let my_num = rand::thread_rng().gen_range(1..=100);
let your_num = rand::thread_rng().gen_range(1..=100);
match my_num.cmp(&your_num) {
Ordering::Less => println!("Your num is bigger than mine."),
Ordering::Greater => println!("My num is bigger than yours"),
Ordering::Equal => {
println!("Our nums are the same...");
break;
}
}
Here we use the Ordering enum
with the cmp
method to handle different cases of a match
expression. Well-named enums
help developers write clearer code. And as we have already covered, the Rust compiler forces a developer to cover all possible outcomes of an expressions. So you're left with readable and bug-resistant code.
What if your tired of using match
There are cases when you may not want to think about all outcomes of a match
expression. The exhaustive nature of a match
expression means you may end up repeating boiler plate code to tell the compiler to do nothing. This is more common when you only have 1 condition you care about matching.
The people who develop Rust considered this case and came up with what is known as if let
syntax. Let's refactor the above example to demonstrate such a case:
use rand::Rng;
use std::cmp::Ordering;
loop {
let my_num = rand::thread_rng().gen_range(1..=100);
let your_num = rand::thread_rng().gen_range(1..=100);
if let Ordering::Equal = my_num.cmp(&your_num) {
println!("Our nums are the same...");
break;
}
}
If we only care about getting matching numbers, then we can use the if let
syntax to move on to the next part of the program. It's worth remembering that the compiler will not help you find all potential outcomes here. So take care when using if let
syntax.
Top comments (0)