2022-01-18 22:40:27 +00:00
|
|
|
|
<!-- DO NOT EDIT THIS FILE.
|
|
|
|
|
|
|
|
|
|
This file is periodically generated from the content in the `/src/`
|
|
|
|
|
directory, so all fixes need to be made in `/src/`.
|
|
|
|
|
-->
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
[TOC]
|
|
|
|
|
|
|
|
|
|
# Enums and Pattern Matching
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
In this chapter, we’ll look at *enumerations*, also referred to as *enums*.
|
|
|
|
|
Enums allow you to define a type by enumerating its possible *variants*. First
|
2021-08-11 00:52:19 +00:00
|
|
|
|
we’ll define and use an enum to show how an enum can encode meaning along with
|
|
|
|
|
data. Next, we’ll explore a particularly useful enum, called `Option`, which
|
|
|
|
|
expresses that a value can be either something or nothing. Then we’ll look at
|
|
|
|
|
how pattern matching in the `match` expression makes it easy to run different
|
|
|
|
|
code for different values of an enum. Finally, we’ll cover how the `if let`
|
2022-01-02 01:38:19 +00:00
|
|
|
|
construct is another convenient and concise idiom available to handle enums in
|
|
|
|
|
your code.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
## Defining an Enum
|
2022-05-26 00:38:58 +00:00
|
|
|
|
|
|
|
|
|
Where structs give you a way of grouping together related fields and data, like
|
|
|
|
|
a `Rectangle` with its `width` and `height`, enums give you a way of saying a
|
|
|
|
|
value is one of a possible set of values. For example, we may want to say that
|
|
|
|
|
`Rectangle` is one of a set of possible shapes that also includes `Circle` and
|
|
|
|
|
`Triangle`. To do this, Rust allows us to encode these possibilities as an enum.
|
|
|
|
|
|
|
|
|
|
Let’s look at a situation we might want to express in code and see why enums
|
|
|
|
|
are useful and more appropriate than structs in this case. Say we need to work
|
|
|
|
|
with IP addresses. Currently, two major standards are used for IP addresses:
|
|
|
|
|
version four and version six. Because these are the only possibilities for an
|
|
|
|
|
IP address that our program will come across, we can *enumerate* all possible
|
|
|
|
|
variants, which is where enumeration gets its name.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Any IP address can be either a version four or a version six address, but not
|
|
|
|
|
both at the same time. That property of IP addresses makes the enum data
|
2022-08-16 14:31:26 +00:00
|
|
|
|
structure appropriate because an enum value can only be one of its variants.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
Both version four and version six addresses are still fundamentally IP
|
|
|
|
|
addresses, so they should be treated as the same type when the code is handling
|
|
|
|
|
situations that apply to any kind of IP address.
|
|
|
|
|
|
|
|
|
|
We can express this concept in code by defining an `IpAddrKind` enumeration and
|
|
|
|
|
listing the possible kinds an IP address can be, `V4` and `V6`. These are the
|
|
|
|
|
variants of the enum:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
enum IpAddrKind {
|
|
|
|
|
V4,
|
|
|
|
|
V6,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`IpAddrKind` is now a custom data type that we can use elsewhere in our code.
|
|
|
|
|
|
|
|
|
|
### Enum Values
|
|
|
|
|
|
|
|
|
|
We can create instances of each of the two variants of `IpAddrKind` like this:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let four = IpAddrKind::V4;
|
|
|
|
|
let six = IpAddrKind::V6;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Note that the variants of the enum are namespaced under its identifier, and we
|
2022-01-02 01:38:19 +00:00
|
|
|
|
use a double colon to separate the two. This is useful because now both values
|
|
|
|
|
`IpAddrKind::V4` and `IpAddrKind::V6` are of the same type: `IpAddrKind`. We
|
|
|
|
|
can then, for instance, define a function that takes any `IpAddrKind`:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn route(ip_kind: IpAddrKind) {}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 14:58:28 +00:00
|
|
|
|
And we can call this function with either variant:
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
```
|
2022-09-13 14:58:28 +00:00
|
|
|
|
route(IpAddrKind::V4);
|
2021-08-11 00:52:19 +00:00
|
|
|
|
route(IpAddrKind::V6);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Using enums has even more advantages. Thinking more about our IP address type,
|
|
|
|
|
at the moment we don’t have a way to store the actual IP address *data*; we
|
|
|
|
|
only know what *kind* it is. Given that you just learned about structs in
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Chapter 5, you might be tempted to tackle this problem with structs as shown in
|
|
|
|
|
Listing 6-1.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
2022-08-16 14:31:26 +00:00
|
|
|
|
1 enum IpAddrKind {
|
2021-08-11 00:52:19 +00:00
|
|
|
|
V4,
|
|
|
|
|
V6,
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
2 struct IpAddr {
|
|
|
|
|
3 kind: IpAddrKind,
|
|
|
|
|
4 address: String,
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
5 let home = IpAddr {
|
2021-08-11 00:52:19 +00:00
|
|
|
|
kind: IpAddrKind::V4,
|
|
|
|
|
address: String::from("127.0.0.1"),
|
|
|
|
|
};
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
6 let loopback = IpAddr {
|
2021-08-11 00:52:19 +00:00
|
|
|
|
kind: IpAddrKind::V6,
|
|
|
|
|
address: String::from("::1"),
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-1: Storing the data and `IpAddrKind` variant of an IP address using a
|
|
|
|
|
`struct`
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
Here, we’ve defined a struct `IpAddr` [2] that has two fields: a `kind` field
|
|
|
|
|
[3] that is of type `IpAddrKind` (the enum we defined previously [1]) and an
|
|
|
|
|
`address` field [4] of type `String`. We have two instances of this struct. The
|
|
|
|
|
first is `home` [5], and it has the value `IpAddrKind::V4` as its `kind` with
|
|
|
|
|
associated address data of `127.0.0.1`. The second instance is `loopback` [6].
|
|
|
|
|
It has the other variant of `IpAddrKind` as its `kind` value, `V6`, and has
|
|
|
|
|
address `::1` associated with it. We’ve used a struct to bundle the `kind` and
|
|
|
|
|
`address` values together, so now the variant is associated with the value.
|
2022-01-02 01:38:19 +00:00
|
|
|
|
|
|
|
|
|
However, representing the same concept using just an enum is more concise:
|
|
|
|
|
rather than an enum inside a struct, we can put data directly into each enum
|
2021-08-11 00:52:19 +00:00
|
|
|
|
variant. This new definition of the `IpAddr` enum says that both `V4` and `V6`
|
|
|
|
|
variants will have associated `String` values:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
enum IpAddr {
|
|
|
|
|
V4(String),
|
|
|
|
|
V6(String),
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
let home = IpAddr::V4(String::from("127.0.0.1"));
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
let loopback = IpAddr::V6(String::from("::1"));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We attach data to each variant of the enum directly, so there is no need for an
|
2022-08-16 14:31:26 +00:00
|
|
|
|
extra struct. Here, it’s also easier to see another detail of how enums work:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
the name of each enum variant that we define also becomes a function that
|
|
|
|
|
constructs an instance of the enum. That is, `IpAddr::V4()` is a function call
|
|
|
|
|
that takes a `String` argument and returns an instance of the `IpAddr` type. We
|
|
|
|
|
automatically get this constructor function defined as a result of defining the
|
|
|
|
|
enum.
|
|
|
|
|
|
|
|
|
|
There’s another advantage to using an enum rather than a struct: each variant
|
2022-08-16 14:31:26 +00:00
|
|
|
|
can have different types and amounts of associated data. Version four IP
|
2021-08-11 00:52:19 +00:00
|
|
|
|
addresses will always have four numeric components that will have values
|
|
|
|
|
between 0 and 255. If we wanted to store `V4` addresses as four `u8` values but
|
|
|
|
|
still express `V6` addresses as one `String` value, we wouldn’t be able to with
|
|
|
|
|
a struct. Enums handle this case with ease:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
enum IpAddr {
|
|
|
|
|
V4(u8, u8, u8, u8),
|
|
|
|
|
V6(String),
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
let home = IpAddr::V4(127, 0, 0, 1);
|
|
|
|
|
|
|
|
|
|
let loopback = IpAddr::V6(String::from("::1"));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We’ve shown several different ways to define data structures to store version
|
|
|
|
|
four and version six IP addresses. However, as it turns out, wanting to store
|
|
|
|
|
IP addresses and encode which kind they are is so common that the standard
|
|
|
|
|
library has a definition we can use! Let’s look at how the standard library
|
|
|
|
|
defines `IpAddr`: it has the exact enum and variants that we’ve defined and
|
|
|
|
|
used, but it embeds the address data inside the variants in the form of two
|
|
|
|
|
different structs, which are defined differently for each variant:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
struct Ipv4Addr {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
--snip--
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
struct Ipv6Addr {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
--snip--
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum IpAddr {
|
|
|
|
|
V4(Ipv4Addr),
|
|
|
|
|
V6(Ipv6Addr),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This code illustrates that you can put any kind of data inside an enum variant:
|
|
|
|
|
strings, numeric types, or structs, for example. You can even include another
|
|
|
|
|
enum! Also, standard library types are often not much more complicated than
|
|
|
|
|
what you might come up with.
|
|
|
|
|
|
|
|
|
|
Note that even though the standard library contains a definition for `IpAddr`,
|
|
|
|
|
we can still create and use our own definition without conflict because we
|
|
|
|
|
haven’t brought the standard library’s definition into our scope. We’ll talk
|
|
|
|
|
more about bringing types into scope in Chapter 7.
|
|
|
|
|
|
|
|
|
|
Let’s look at another example of an enum in Listing 6-2: this one has a wide
|
|
|
|
|
variety of types embedded in its variants.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
enum Message {
|
|
|
|
|
Quit,
|
|
|
|
|
Move { x: i32, y: i32 },
|
|
|
|
|
Write(String),
|
|
|
|
|
ChangeColor(i32, i32, i32),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-2: A `Message` enum whose variants each store different amounts and
|
|
|
|
|
types of values
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
This enum has four variants with different types:
|
|
|
|
|
|
2022-09-13 15:00:52 +00:00
|
|
|
|
* `Quit` has no data associated with it at all.
|
|
|
|
|
* `Move` has named fields, like a struct does.
|
|
|
|
|
* `Write` includes a single `String`.
|
|
|
|
|
* `ChangeColor` includes three `i32` values.
|
|
|
|
|
|
|
|
|
|
Defining an enum with variants such as the ones in Listing 6-2 is similar to
|
|
|
|
|
defining different kinds of struct definitions, except the enum doesn’t use the
|
|
|
|
|
`struct` keyword and all the variants are grouped together under the `Message`
|
|
|
|
|
type. The following structs could hold the same data that the preceding enum
|
|
|
|
|
variants hold:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
struct QuitMessage; // unit struct
|
|
|
|
|
struct MoveMessage {
|
|
|
|
|
x: i32,
|
|
|
|
|
y: i32,
|
|
|
|
|
}
|
|
|
|
|
struct WriteMessage(String); // tuple struct
|
|
|
|
|
struct ChangeColorMessage(i32, i32, i32); // tuple struct
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
But if we used the different structs, each of which has its own type, we
|
2021-08-11 00:52:19 +00:00
|
|
|
|
couldn’t as easily define a function to take any of these kinds of messages as
|
|
|
|
|
we could with the `Message` enum defined in Listing 6-2, which is a single type.
|
|
|
|
|
|
|
|
|
|
There is one more similarity between enums and structs: just as we’re able to
|
|
|
|
|
define methods on structs using `impl`, we’re also able to define methods on
|
|
|
|
|
enums. Here’s a method named `call` that we could define on our `Message` enum:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
impl Message {
|
|
|
|
|
fn call(&self) {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
1 // method body would be defined here
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
|
|
|
2 let m = Message::Write(String::from("hello"));
|
2021-08-11 00:52:19 +00:00
|
|
|
|
m.call();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The body of the method would use `self` to get the value that we called the
|
2022-08-16 14:31:26 +00:00
|
|
|
|
method on. In this example, we’ve created a variable `m` [2] that has the value
|
2021-08-11 00:52:19 +00:00
|
|
|
|
`Message::Write(String::from("hello"))`, and that is what `self` will be in the
|
2022-08-16 14:31:26 +00:00
|
|
|
|
body of the `call` method [1] when `m.call()` runs.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Let’s look at another enum in the standard library that is very common and
|
|
|
|
|
useful: `Option`.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
### The Option Enum and Its Advantages Over Null Values
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
This section explores a case study of `Option`, which is another enum defined
|
2022-01-02 01:38:19 +00:00
|
|
|
|
by the standard library. The `Option` type encodes the very common scenario in
|
2022-01-04 02:34:13 +00:00
|
|
|
|
which a value could be something or it could be nothing.
|
2022-05-26 00:38:58 +00:00
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
For example, if you request the first item in a list containing multiple items,
|
|
|
|
|
you would get a value. If you request the first item in an empty list, you
|
|
|
|
|
would get nothing. Expressing this concept in terms of the type system means
|
|
|
|
|
the compiler can check whether you’ve handled all the cases you should be
|
|
|
|
|
handling; this functionality can prevent bugs that are extremely common in
|
|
|
|
|
other programming languages.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Programming language design is often thought of in terms of which features you
|
|
|
|
|
include, but the features you exclude are important too. Rust doesn’t have the
|
|
|
|
|
null feature that many other languages have. *Null* is a value that means there
|
|
|
|
|
is no value there. In languages with null, variables can always be in one of
|
|
|
|
|
two states: null or not-null.
|
|
|
|
|
|
|
|
|
|
In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony
|
|
|
|
|
Hoare, the inventor of null, has this to say:
|
|
|
|
|
|
2022-09-13 15:01:40 +00:00
|
|
|
|
> I call it my billion-dollar mistake. At that time, I was designing the first
|
|
|
|
|
comprehensive type system for references in an object-oriented language. My
|
|
|
|
|
goal was to ensure that all use of references should be absolutely safe, with
|
|
|
|
|
checking performed automatically by the compiler. But I couldn’t resist the
|
|
|
|
|
temptation to put in a null reference, simply because it was so easy to
|
|
|
|
|
implement. This has led to innumerable errors, vulnerabilities, and system
|
|
|
|
|
crashes, which have probably caused a billion dollars of pain and damage in the
|
|
|
|
|
last forty years.The problem with null values is that if you try to use a null
|
|
|
|
|
value as a not-null value, you’ll get an error of some kind. Because this null
|
|
|
|
|
or not-null property is pervasive, it’s extremely easy to make this kind of
|
|
|
|
|
error.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
However, the concept that null is trying to express is still a useful one: a
|
|
|
|
|
null is a value that is currently invalid or absent for some reason.
|
|
|
|
|
|
|
|
|
|
The problem isn’t really with the concept but with the particular
|
|
|
|
|
implementation. As such, Rust does not have nulls, but it does have an enum
|
|
|
|
|
that can encode the concept of a value being present or absent. This enum is
|
2022-08-16 14:31:26 +00:00
|
|
|
|
`Option<T>`, and it is defined by the standard library as follows:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
enum Option<T> {
|
|
|
|
|
None,
|
|
|
|
|
Some(T),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `Option<T>` enum is so useful that it’s even included in the prelude; you
|
2022-01-02 01:38:19 +00:00
|
|
|
|
don’t need to bring it into scope explicitly. Its variants are also included in
|
|
|
|
|
the prelude: you can use `Some` and `None` directly without the `Option::`
|
|
|
|
|
prefix. The `Option<T>` enum is still just a regular enum, and `Some(T)` and
|
|
|
|
|
`None` are still variants of type `Option<T>`.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
The `<T>` syntax is a feature of Rust we haven’t talked about yet. It’s a
|
|
|
|
|
generic type parameter, and we’ll cover generics in more detail in Chapter 10.
|
2022-08-16 14:31:26 +00:00
|
|
|
|
For now, all you need to know is that `<T>` means that the `Some` variant of
|
|
|
|
|
the `Option` enum can hold one piece of data of any type, and that each
|
|
|
|
|
concrete type that gets used in place of `T` makes the overall `Option<T>` type
|
|
|
|
|
a different type. Here are some examples of using `Option` values to hold
|
|
|
|
|
number types and string types:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let some_number = Some(5);
|
2022-05-26 00:38:58 +00:00
|
|
|
|
let some_char = Some('e');
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
let absent_number: Option<i32> = None;
|
|
|
|
|
```
|
|
|
|
|
|
2022-05-26 00:38:58 +00:00
|
|
|
|
The type of `some_number` is `Option<i32>`. The type of `some_char` is
|
|
|
|
|
`Option<char>`, which is a different type. Rust can infer these types because
|
2021-08-11 00:52:19 +00:00
|
|
|
|
we’ve specified a value inside the `Some` variant. For `absent_number`, Rust
|
|
|
|
|
requires us to annotate the overall `Option` type: the compiler can’t infer the
|
|
|
|
|
type that the corresponding `Some` variant will hold by looking only at a
|
|
|
|
|
`None` value. Here, we tell Rust that we mean for `absent_number` to be of type
|
|
|
|
|
`Option<i32>`.
|
|
|
|
|
|
|
|
|
|
When we have a `Some` value, we know that a value is present and the value is
|
2022-08-16 14:31:26 +00:00
|
|
|
|
held within the `Some`. When we have a `None` value, in some sense it means the
|
|
|
|
|
same thing as null: we don’t have a valid value. So why is having `Option<T>`
|
|
|
|
|
any better than having null?
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
In short, because `Option<T>` and `T` (where `T` can be any type) are different
|
|
|
|
|
types, the compiler won’t let us use an `Option<T>` value as if it were
|
2022-08-16 14:31:26 +00:00
|
|
|
|
definitely a valid value. For example, this code won’t compile, because it’s
|
2021-08-11 00:52:19 +00:00
|
|
|
|
trying to add an `i8` to an `Option<i8>`:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let x: i8 = 5;
|
|
|
|
|
let y: Option<i8> = Some(5);
|
|
|
|
|
|
|
|
|
|
let sum = x + y;
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
If we run this code, we get an error message like this one:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
error[E0277]: cannot add `Option<i8>` to `i8`
|
|
|
|
|
--> src/main.rs:5:17
|
|
|
|
|
|
|
|
|
|
|
5 | let sum = x + y;
|
|
|
|
|
| ^ no implementation for `i8 + Option<i8>`
|
|
|
|
|
|
|
|
|
|
|
= help: the trait `Add<Option<i8>>` is not implemented for `i8`
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Intense! In effect, this error message means that Rust doesn’t understand how
|
|
|
|
|
to add an `i8` and an `Option<i8>`, because they’re different types. When we
|
|
|
|
|
have a value of a type like `i8` in Rust, the compiler will ensure that we
|
|
|
|
|
always have a valid value. We can proceed confidently without having to check
|
|
|
|
|
for null before using that value. Only when we have an `Option<i8>` (or
|
|
|
|
|
whatever type of value we’re working with) do we have to worry about possibly
|
|
|
|
|
not having a value, and the compiler will make sure we handle that case before
|
|
|
|
|
using the value.
|
|
|
|
|
|
|
|
|
|
In other words, you have to convert an `Option<T>` to a `T` before you can
|
|
|
|
|
perform `T` operations with it. Generally, this helps catch one of the most
|
2022-08-16 14:31:26 +00:00
|
|
|
|
common issues with null: assuming that something isn’t null when it actually is.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Eliminating the risk of incorrectly assuming a not-null value helps you to be
|
2021-08-11 00:52:19 +00:00
|
|
|
|
more confident in your code. In order to have a value that can possibly be
|
|
|
|
|
null, you must explicitly opt in by making the type of that value `Option<T>`.
|
|
|
|
|
Then, when you use that value, you are required to explicitly handle the case
|
|
|
|
|
when the value is null. Everywhere that a value has a type that isn’t an
|
|
|
|
|
`Option<T>`, you *can* safely assume that the value isn’t null. This was a
|
|
|
|
|
deliberate design decision for Rust to limit null’s pervasiveness and increase
|
|
|
|
|
the safety of Rust code.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
So how do you get the `T` value out of a `Some` variant when you have a value
|
|
|
|
|
of type `Option<T>` so that you can use that value? The `Option<T>` enum has a
|
|
|
|
|
large number of methods that are useful in a variety of situations; you can
|
|
|
|
|
check them out in its documentation. Becoming familiar with the methods on
|
2021-08-11 00:52:19 +00:00
|
|
|
|
`Option<T>` will be extremely useful in your journey with Rust.
|
|
|
|
|
|
|
|
|
|
In general, in order to use an `Option<T>` value, you want to have code that
|
|
|
|
|
will handle each variant. You want some code that will run only when you have a
|
|
|
|
|
`Some(T)` value, and this code is allowed to use the inner `T`. You want some
|
2022-08-16 14:31:26 +00:00
|
|
|
|
other code to run only if you have a `None` value, and that code doesn’t have a
|
|
|
|
|
`T` value available. The `match` expression is a control flow construct that
|
|
|
|
|
does just this when used with enums: it will run different code depending on
|
|
|
|
|
which variant of the enum it has, and that code can use the data inside the
|
|
|
|
|
matching value.
|
|
|
|
|
|
|
|
|
|
## The match Control Flow Construct
|
|
|
|
|
|
|
|
|
|
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; Chapter 18 covers all the
|
|
|
|
|
different kinds of patterns and what they do. The power of `match` comes from
|
|
|
|
|
the expressiveness of the patterns and the fact that the compiler confirms that
|
|
|
|
|
all possible cases are handled.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Think of a `match` expression as being like a coin-sorting machine: coins slide
|
|
|
|
|
down a track with variously sized holes along it, and each coin falls through
|
|
|
|
|
the first hole it encounters that it fits into. In the same way, values go
|
|
|
|
|
through each pattern in a `match`, and at the first pattern the value “fits,”
|
|
|
|
|
the value falls into the associated code block to be used during execution.
|
|
|
|
|
|
2022-05-26 00:38:58 +00:00
|
|
|
|
Speaking of coins, let’s use them as an example using `match`! We can write a
|
2022-08-16 14:31:26 +00:00
|
|
|
|
function that takes an unknown US coin and, in a similar way as the counting
|
|
|
|
|
machine, determines which coin it is and returns its value in cents, as shown
|
|
|
|
|
in Listing 6-3.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
1 enum Coin {
|
2021-08-11 00:52:19 +00:00
|
|
|
|
Penny,
|
|
|
|
|
Nickel,
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Dime,
|
2021-08-11 00:52:19 +00:00
|
|
|
|
Quarter,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn value_in_cents(coin: Coin) -> u8 {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
2 match coin {
|
|
|
|
|
3 Coin::Penny => 1,
|
2021-08-11 00:52:19 +00:00
|
|
|
|
Coin::Nickel => 5,
|
|
|
|
|
Coin::Dime => 10,
|
|
|
|
|
Coin::Quarter => 25,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-3: An enum and a `match` expression that has the variants of the enum
|
|
|
|
|
as its patterns
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Let’s break down the `match` in the `value_in_cents` function. First we list
|
2021-08-11 00:52:19 +00:00
|
|
|
|
the `match` keyword followed by an expression, which in this case is the value
|
2022-08-16 14:31:26 +00:00
|
|
|
|
`coin` [2]. This seems very similar to an expression used with `if`, but
|
|
|
|
|
there’s a big difference: with `if`, the expression needs to return a Boolean
|
|
|
|
|
value, but here it can return any type. The type of `coin` in this example is
|
|
|
|
|
the `Coin` enum that we defined at [1].
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Next are the `match` arms. An arm has two parts: a pattern and some code. The
|
|
|
|
|
first arm here has a pattern that is the value `Coin::Penny` and then the `=>`
|
2022-08-16 14:31:26 +00:00
|
|
|
|
operator that separates the pattern and the code to run [3]. The code in this
|
|
|
|
|
case is just the value `1`. Each arm is separated from the next with a comma.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
When the `match` expression executes, it compares the resultant value against
|
2021-08-11 00:52:19 +00:00
|
|
|
|
the pattern of each arm, in order. If a pattern matches the value, the code
|
|
|
|
|
associated with that pattern is executed. If that pattern doesn’t match the
|
|
|
|
|
value, execution continues to the next arm, much as in a coin-sorting machine.
|
|
|
|
|
We can have as many arms as we need: in Listing 6-3, our `match` has four arms.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
The code associated with each arm is an expression, and the resultant value of
|
2021-08-11 00:52:19 +00:00
|
|
|
|
the expression in the matching arm is the value that gets returned for the
|
|
|
|
|
entire `match` expression.
|
|
|
|
|
|
2022-08-16 01:13:08 +00:00
|
|
|
|
We don’t typically use curly brackets if the match arm code is short, as it is
|
2021-08-11 00:52:19 +00:00
|
|
|
|
in Listing 6-3 where each arm just returns a value. If you want to run multiple
|
2022-05-26 00:38:58 +00:00
|
|
|
|
lines of code in a match arm, you must use curly brackets, and the comma
|
|
|
|
|
following the arm is then optional. For example, the following code prints
|
|
|
|
|
“Lucky penny!” every time the method is called with a `Coin::Penny`, but still
|
|
|
|
|
returns the last value of the block, `1`:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn value_in_cents(coin: Coin) -> u8 {
|
|
|
|
|
match coin {
|
|
|
|
|
Coin::Penny => {
|
|
|
|
|
println!("Lucky penny!");
|
|
|
|
|
1
|
|
|
|
|
}
|
|
|
|
|
Coin::Nickel => 5,
|
|
|
|
|
Coin::Dime => 10,
|
|
|
|
|
Coin::Quarter => 25,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
### Patterns That Bind to Values
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Another useful feature of match arms is that they can bind to the parts of the
|
|
|
|
|
values that match the pattern. This is how we can extract values out of enum
|
|
|
|
|
variants.
|
|
|
|
|
|
|
|
|
|
As an example, let’s change one of our enum variants to hold data inside it.
|
|
|
|
|
From 1999 through 2008, the United States minted quarters with different
|
|
|
|
|
designs for each of the 50 states on one side. No other coins got state
|
|
|
|
|
designs, so only quarters have this extra value. We can add this information to
|
2022-08-16 14:31:26 +00:00
|
|
|
|
our `enum` by changing the `Quarter` variant to include a `UsState` value
|
|
|
|
|
stored inside it, which we’ve done in Listing 6-4.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
#[derive(Debug)] // so we can inspect the state in a minute
|
|
|
|
|
enum UsState {
|
|
|
|
|
Alabama,
|
|
|
|
|
Alaska,
|
2022-08-16 14:31:26 +00:00
|
|
|
|
--snip--
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
enum Coin {
|
|
|
|
|
Penny,
|
|
|
|
|
Nickel,
|
|
|
|
|
Dime,
|
|
|
|
|
Quarter(UsState),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-4: A `Coin` enum in which the `Quarter` variant also holds a
|
|
|
|
|
`UsState` value
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Let’s imagine that a friend is trying to collect all 50 state quarters. While
|
|
|
|
|
we sort our loose change by coin type, we’ll also call out the name of the
|
2022-08-16 14:31:26 +00:00
|
|
|
|
state associated with each quarter so that if it’s one our friend doesn’t have,
|
|
|
|
|
they can add it to their collection.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
In the match expression for this code, we add a variable called `state` to the
|
|
|
|
|
pattern that matches values of the variant `Coin::Quarter`. When a
|
|
|
|
|
`Coin::Quarter` matches, the `state` variable will bind to the value of that
|
|
|
|
|
quarter’s state. Then we can use `state` in the code for that arm, like so:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn value_in_cents(coin: Coin) -> u8 {
|
|
|
|
|
match coin {
|
|
|
|
|
Coin::Penny => 1,
|
|
|
|
|
Coin::Nickel => 5,
|
|
|
|
|
Coin::Dime => 10,
|
|
|
|
|
Coin::Quarter(state) => {
|
|
|
|
|
println!("State quarter from {:?}!", state);
|
|
|
|
|
25
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If we were to call `value_in_cents(Coin::Quarter(UsState::Alaska))`, `coin`
|
|
|
|
|
would be `Coin::Quarter(UsState::Alaska)`. When we compare that value with each
|
|
|
|
|
of the match arms, none of them match until we reach `Coin::Quarter(state)`. At
|
|
|
|
|
that point, the binding for `state` will be the value `UsState::Alaska`. We can
|
|
|
|
|
then use that binding in the `println!` expression, thus getting the inner
|
|
|
|
|
state value out of the `Coin` enum variant for `Quarter`.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
### Matching with Option<T>
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
In the previous section, we wanted to get the inner `T` value out of the `Some`
|
2022-08-24 13:12:29 +00:00
|
|
|
|
case when using `Option<T>`; we can also handle `Option<T>` using `match`, as
|
|
|
|
|
we did with the `Coin` enum! Instead of comparing coins, we’ll compare the
|
|
|
|
|
variants of `Option<T>`, but the way the `match` expression works remains the
|
|
|
|
|
same.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Let’s say we want to write a function that takes an `Option<i32>` and, if
|
|
|
|
|
there’s a value inside, adds 1 to that value. If there isn’t a value inside,
|
|
|
|
|
the function should return the `None` value and not attempt to perform any
|
|
|
|
|
operations.
|
|
|
|
|
|
|
|
|
|
This function is very easy to write, thanks to `match`, and will look like
|
|
|
|
|
Listing 6-5.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn plus_one(x: Option<i32>) -> Option<i32> {
|
|
|
|
|
match x {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
1 None => None,
|
|
|
|
|
2 Some(i) => Some(i + 1),
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let five = Some(5);
|
2022-08-16 14:31:26 +00:00
|
|
|
|
let six = plus_one(five); 3
|
|
|
|
|
let none = plus_one(None); 4
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-5: A function that uses a `match` expression on an `Option<i32>`
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Let’s examine the first execution of `plus_one` in more detail. When we call
|
2022-08-16 14:31:26 +00:00
|
|
|
|
`plus_one(five)` [3], the variable `x` in the body of `plus_one` will have the
|
|
|
|
|
value `Some(5)`. We then compare that against each match arm:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
None => None,
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
The `Some(5)` value doesn’t match the pattern `None` [1], so we continue to the
|
|
|
|
|
next arm:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
Some(i) => Some(i + 1),
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Does `Some(5)` match `Some(i)` [2]? Why yes, it does! We have the same variant.
|
|
|
|
|
The `i` binds to the value contained in `Some`, so `i` takes the value `5`. The
|
2021-08-11 00:52:19 +00:00
|
|
|
|
code in the match arm is then executed, so we add 1 to the value of `i` and
|
|
|
|
|
create a new `Some` value with our total `6` inside.
|
|
|
|
|
|
|
|
|
|
Now let’s consider the second call of `plus_one` in Listing 6-5, where `x` is
|
2022-08-16 14:31:26 +00:00
|
|
|
|
`None` [4]. We enter the `match` and compare to the first arm [1].
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
It matches! There’s no value to add to, so the program stops and returns the
|
|
|
|
|
`None` value on the right side of `=>`. Because the first arm matched, no other
|
|
|
|
|
arms are compared.
|
|
|
|
|
|
|
|
|
|
Combining `match` and enums is useful in many situations. You’ll see this
|
|
|
|
|
pattern a lot in Rust code: `match` against an enum, bind a variable to the
|
|
|
|
|
data inside, and then execute code based on it. It’s a bit tricky at first, but
|
|
|
|
|
once you get used to it, you’ll wish you had it in all languages. It’s
|
|
|
|
|
consistently a user favorite.
|
|
|
|
|
|
|
|
|
|
### Matches Are Exhaustive
|
2022-05-26 00:23:58 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
There’s one other aspect of `match` we need to discuss: the arms’ patterns must
|
|
|
|
|
cover all possibilities. Consider this version of our `plus_one` function,
|
|
|
|
|
which has a bug and won’t compile:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
fn plus_one(x: Option<i32>) -> Option<i32> {
|
|
|
|
|
match x {
|
|
|
|
|
Some(i) => Some(i + 1),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
We didn’t handle the `None` case, so this code will cause a bug. Luckily, it’s
|
|
|
|
|
a bug Rust knows how to catch. If we try to compile this code, we’ll get this
|
|
|
|
|
error:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
error[E0004]: non-exhaustive patterns: `None` not covered
|
2022-08-16 14:31:26 +00:00
|
|
|
|
--> src/main.rs:3:15
|
|
|
|
|
|
|
|
|
|
|
3 | match x {
|
|
|
|
|
| ^ pattern `None` not covered
|
|
|
|
|
|
|
|
|
|
|
note: `Option<i32>` defined here
|
|
|
|
|
= note: the matched value is of type `Option<i32>`
|
|
|
|
|
help: ensure that all possible cases are being handled by adding
|
|
|
|
|
a match arm with a wildcard pattern or an explicit pattern as
|
|
|
|
|
shown
|
2022-08-16 01:13:08 +00:00
|
|
|
|
|
|
|
|
|
|
4 ~ Some(i) => Some(i + 1),
|
|
|
|
|
5 ~ None => todo!(),
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
```
|
|
|
|
|
|
2022-08-24 13:12:29 +00:00
|
|
|
|
Rust knows that we didn’t cover every possible case, and even knows which
|
2021-08-11 00:52:19 +00:00
|
|
|
|
pattern we forgot! Matches in Rust are *exhaustive*: we must exhaust every last
|
|
|
|
|
possibility in order for the code to be valid. Especially in the case of
|
|
|
|
|
`Option<T>`, when Rust prevents us from forgetting to explicitly handle the
|
|
|
|
|
`None` case, it protects us from assuming that we have a value when we might
|
|
|
|
|
have null, thus making the billion-dollar mistake discussed earlier impossible.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
### Catch-all Patterns and the _ Placeholder
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Using enums, we can also take special actions for a few particular values, but
|
|
|
|
|
for all other values take one default action. Imagine we’re implementing a game
|
|
|
|
|
where, if you roll a 3 on a dice roll, your player doesn’t move, but instead
|
|
|
|
|
gets a new fancy hat. If you roll a 7, your player loses a fancy hat. For all
|
|
|
|
|
other values, your player moves that number of spaces on the game board. Here’s
|
|
|
|
|
a `match` that implements that logic, with the result of the dice roll
|
|
|
|
|
hardcoded rather than a random value, and all other logic represented by
|
|
|
|
|
functions without bodies because actually implementing them is out of scope for
|
|
|
|
|
this example:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let dice_roll = 9;
|
|
|
|
|
match dice_roll {
|
|
|
|
|
3 => add_fancy_hat(),
|
|
|
|
|
7 => remove_fancy_hat(),
|
2022-08-16 14:31:26 +00:00
|
|
|
|
1 other => move_player(other),
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_fancy_hat() {}
|
|
|
|
|
fn remove_fancy_hat() {}
|
|
|
|
|
fn move_player(num_spaces: u8) {}
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
For the first two arms, the patterns are the literal values `3` and `7`. For
|
|
|
|
|
the last arm that covers every other possible value, the pattern is the
|
|
|
|
|
variable we’ve chosen to name `other` [1]. The code that runs for the `other`
|
|
|
|
|
arm uses the variable by passing it to the `move_player` function.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
This code compiles, even though we haven’t listed all the possible values a
|
|
|
|
|
`u8` can have, because the last pattern will match all values not specifically
|
|
|
|
|
listed. This catch-all pattern meets the requirement that `match` must be
|
|
|
|
|
exhaustive. Note that we have to put the catch-all arm last because the
|
2022-01-02 01:38:19 +00:00
|
|
|
|
patterns are evaluated in order. If we put the catch-all arm earlier, the other
|
|
|
|
|
arms would never run, so Rust will warn us if we add arms after a catch-all!
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Rust also has a pattern we can use when we want a catch-all but don’t want to
|
|
|
|
|
*use* the value in the catch-all pattern: `_` is a special pattern that matches
|
|
|
|
|
any value and does not bind to that value. This tells Rust we aren’t going to
|
|
|
|
|
use the value, so Rust won’t warn us about an unused variable.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
Let’s change the rules of the game: now, if you roll anything other than a 3 or
|
|
|
|
|
a 7, you must roll again. We no longer need to use the catch-all value, so we
|
|
|
|
|
can change our code to use `_` instead of the variable named `other`:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let dice_roll = 9;
|
|
|
|
|
match dice_roll {
|
|
|
|
|
3 => add_fancy_hat(),
|
|
|
|
|
7 => remove_fancy_hat(),
|
|
|
|
|
_ => reroll(),
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
fn add_fancy_hat() {}
|
|
|
|
|
fn remove_fancy_hat() {}
|
|
|
|
|
fn reroll() {}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This example also meets the exhaustiveness requirement because we’re explicitly
|
|
|
|
|
ignoring all other values in the last arm; we haven’t forgotten anything.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Finally, we’ll change the rules of the game one more time so that nothing else
|
2022-01-02 01:38:19 +00:00
|
|
|
|
happens on your turn if you roll anything other than a 3 or a 7. We can express
|
|
|
|
|
that by using the unit value (the empty tuple type we mentioned in “The Tuple
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Type” on page XX) as the code that goes with the `_` arm:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let dice_roll = 9;
|
|
|
|
|
match dice_roll {
|
|
|
|
|
3 => add_fancy_hat(),
|
|
|
|
|
7 => remove_fancy_hat(),
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|
2021-08-11 00:52:19 +00:00
|
|
|
|
fn add_fancy_hat() {}
|
|
|
|
|
fn remove_fancy_hat() {}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Here, we’re telling Rust explicitly that we aren’t going to use any other value
|
|
|
|
|
that doesn’t match a pattern in an earlier arm, and we don’t want to run any
|
|
|
|
|
code in this case.
|
|
|
|
|
|
|
|
|
|
There’s more about patterns and matching that we’ll cover in Chapter 18. For
|
|
|
|
|
now, we’re going to move on to the `if let` syntax, which can be useful in
|
|
|
|
|
situations where the `match` expression is a bit wordy.
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
## Concise Control Flow with if let
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
The `if let` syntax lets you combine `if` and `let` into a less verbose way to
|
|
|
|
|
handle values that match one pattern while ignoring the rest. Consider the
|
2022-08-16 14:31:26 +00:00
|
|
|
|
program in Listing 6-6 that matches on an `Option<u8>` value in the
|
|
|
|
|
`config_max` variable but only wants to execute code if the value is the `Some`
|
|
|
|
|
variant.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let config_max = Some(3u8);
|
|
|
|
|
match config_max {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Some(max) => println!("The maximum is configured to be {max}"),
|
2021-08-11 00:52:19 +00:00
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-13 16:54:09 +00:00
|
|
|
|
Listing 6-6: A `match` that only cares about executing code when the value is
|
|
|
|
|
`Some`
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
2022-01-02 01:38:19 +00:00
|
|
|
|
If the value is `Some`, we print out the value in the `Some` variant by binding
|
|
|
|
|
the value to the variable `max` in the pattern. We don’t want to do anything
|
|
|
|
|
with the `None` value. To satisfy the `match` expression, we have to add `_ =>
|
|
|
|
|
()` after processing just one variant, which is annoying boilerplate code to
|
|
|
|
|
add.
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
Instead, we could write this in a shorter way using `if let`. The following
|
|
|
|
|
code behaves the same as the `match` in Listing 6-6:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let config_max = Some(3u8);
|
|
|
|
|
if let Some(max) = config_max {
|
2022-08-16 14:31:26 +00:00
|
|
|
|
println!("The maximum is configured to be {max}");
|
2021-08-11 00:52:19 +00:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The syntax `if let` takes a pattern and an expression separated by an equal
|
|
|
|
|
sign. It works the same way as a `match`, where the expression is given to the
|
|
|
|
|
`match` and the pattern is its first arm. In this case, the pattern is
|
|
|
|
|
`Some(max)`, and the `max` binds to the value inside the `Some`. We can then
|
2022-08-24 13:12:29 +00:00
|
|
|
|
use `max` in the body of the `if let` block in the same way we used `max` in
|
2021-08-11 00:52:19 +00:00
|
|
|
|
the corresponding `match` arm. The code in the `if let` block isn’t run if the
|
|
|
|
|
value doesn’t match the pattern.
|
|
|
|
|
|
|
|
|
|
Using `if let` means less typing, less indentation, and less boilerplate code.
|
|
|
|
|
However, you lose the exhaustive checking that `match` enforces. Choosing
|
|
|
|
|
between `match` and `if let` depends on what you’re doing in your particular
|
|
|
|
|
situation and whether gaining conciseness is an appropriate trade-off for
|
|
|
|
|
losing exhaustive checking.
|
|
|
|
|
|
|
|
|
|
In other words, you can think of `if let` as syntax sugar for a `match` that
|
|
|
|
|
runs code when the value matches one pattern and then ignores all other values.
|
|
|
|
|
|
|
|
|
|
We can include an `else` with an `if let`. The block of code that goes with the
|
|
|
|
|
`else` is the same as the block of code that would go with the `_` case in the
|
|
|
|
|
`match` expression that is equivalent to the `if let` and `else`. Recall the
|
|
|
|
|
`Coin` enum definition in Listing 6-4, where the `Quarter` variant also held a
|
|
|
|
|
`UsState` value. If we wanted to count all non-quarter coins we see while also
|
|
|
|
|
announcing the state of the quarters, we could do that with a `match`
|
2022-08-16 14:31:26 +00:00
|
|
|
|
expression, like this:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
match coin {
|
|
|
|
|
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
|
|
|
|
|
_ => count += 1,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2022-08-16 14:31:26 +00:00
|
|
|
|
Or we could use an `if let` and `else` expression, like this:
|
2021-08-11 00:52:19 +00:00
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
let mut count = 0;
|
|
|
|
|
if let Coin::Quarter(state) = coin {
|
|
|
|
|
println!("State quarter from {:?}!", state);
|
|
|
|
|
} else {
|
|
|
|
|
count += 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If you have a situation in which your program has logic that is too verbose to
|
|
|
|
|
express using a `match`, remember that `if let` is in your Rust toolbox as well.
|
|
|
|
|
|
|
|
|
|
## Summary
|
|
|
|
|
|
|
|
|
|
We’ve now covered how to use enums to create custom types that can be one of a
|
|
|
|
|
set of enumerated values. We’ve shown how the standard library’s `Option<T>`
|
|
|
|
|
type helps you use the type system to prevent errors. When enum values have
|
|
|
|
|
data inside them, you can use `match` or `if let` to extract and use those
|
|
|
|
|
values, depending on how many cases you need to handle.
|
|
|
|
|
|
|
|
|
|
Your Rust programs can now express concepts in your domain using structs and
|
|
|
|
|
enums. Creating custom types to use in your API ensures type safety: the
|
2022-08-16 14:31:26 +00:00
|
|
|
|
compiler will make certain your functions only get values of the type each
|
2021-08-11 00:52:19 +00:00
|
|
|
|
function expects.
|
|
|
|
|
|
|
|
|
|
In order to provide a well-organized API to your users that is straightforward
|
|
|
|
|
to use and only exposes exactly what your users will need, let’s now turn to
|
|
|
|
|
Rust’s modules.
|
2022-08-16 14:31:26 +00:00
|
|
|
|
|