feat: added cleanscript

This commit is contained in:
2026-06-22 21:27:36 +05:30
parent dbddc0ce2d
commit 4581eea409
309 changed files with 14551 additions and 46035 deletions

19
ch06/ch06-00-enums.html Normal file
View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Enums and Pattern Matching</title>
</head>
<body>
<h1 id="enums-and-pattern-matching"><a class="header" href="#enums-and-pattern-matching">Enums and Pattern Matching</a></h1>
<p>In this chapter, well look at enumerations, also referred to as <em>enums</em>.
Enums allow you to define a type by enumerating its possible variants. First
well define and use an enum to show how an enum can encode meaning along with
data. Next, well explore a particularly useful enum, called <code>Option</code>, which
expresses that a value can be either something or nothing. Then, well look at
how pattern matching in the <code>match</code> expression makes it easy to run different
code for different values of an enum. Finally, well cover how the <code>if let</code>
construct is another convenient and concise idiom available to handle enums in
your code.</p>
</body>
</html>

View File

@@ -0,0 +1,399 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Defining an Enum</title>
</head>
<body>
<h2 id="defining-an-enum"><a class="header" href="#defining-an-enum">Defining an Enum</a></h2>
<p>Where structs give you a way of grouping together related fields and data, like
a <code>Rectangle</code> with its <code>width</code> and <code>height</code>, 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
<code>Rectangle</code> is one of a set of possible shapes that also includes <code>Circle</code> and
<code>Triangle</code>. To do this, Rust allows us to encode these possibilities as an enum.</p>
<p>Lets 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 <em>enumerate</em> all possible
variants, which is where enumeration gets its name.</p>
<p>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
structure appropriate because an enum value can only be one of its variants.
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.</p>
<p>We can express this concept in code by defining an <code>IpAddrKind</code> enumeration and
listing the possible kinds an IP address can be, <code>V4</code> and <code>V6</code>. These are the
variants of the enum:</p>
<pre class="playground"><code class="language-rust edition2024">enum IpAddrKind {
V4,
V6,
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let four = IpAddrKind::V4;
</span><span class="boring"> let six = IpAddrKind::V6;
</span><span class="boring">
</span><span class="boring"> route(IpAddrKind::V4);
</span><span class="boring"> route(IpAddrKind::V6);
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn route(ip_kind: IpAddrKind) {}</span></code></pre>
<p><code>IpAddrKind</code> is now a custom data type that we can use elsewhere in our code.</p>
<h3 id="enum-values"><a class="header" href="#enum-values">Enum Values</a></h3>
<p>We can create instances of each of the two variants of <code>IpAddrKind</code> like this:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">enum IpAddrKind {
</span><span class="boring"> V4,
</span><span class="boring"> V6,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span> let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
<span class="boring">
</span><span class="boring"> route(IpAddrKind::V4);
</span><span class="boring"> route(IpAddrKind::V6);
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn route(ip_kind: IpAddrKind) {}</span></code></pre>
<p>Note that the variants of the enum are namespaced under its identifier, and we
use a double colon to separate the two. This is useful because now both values
<code>IpAddrKind::V4</code> and <code>IpAddrKind::V6</code> are of the same type: <code>IpAddrKind</code>. We
can then, for instance, define a function that takes any <code>IpAddrKind</code>:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">enum IpAddrKind {
</span><span class="boring"> V4,
</span><span class="boring"> V6,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let four = IpAddrKind::V4;
</span><span class="boring"> let six = IpAddrKind::V6;
</span><span class="boring">
</span><span class="boring"> route(IpAddrKind::V4);
</span><span class="boring"> route(IpAddrKind::V6);
</span><span class="boring">}
</span><span class="boring">
</span>fn route(ip_kind: IpAddrKind) {}</code></pre>
<p>And we can call this function with either variant:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">enum IpAddrKind {
</span><span class="boring"> V4,
</span><span class="boring"> V6,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let four = IpAddrKind::V4;
</span><span class="boring"> let six = IpAddrKind::V6;
</span><span class="boring">
</span> route(IpAddrKind::V4);
route(IpAddrKind::V6);
<span class="boring">}
</span><span class="boring">
</span><span class="boring">fn route(ip_kind: IpAddrKind) {}</span></code></pre>
<p>Using enums has even more advantages. Thinking more about our IP address type,
at the moment we dont have a way to store the actual IP address <em>data</em>; we
only know what <em>kind</em> it is. Given that you just learned about structs in
Chapter 5, you might be tempted to tackle this problem with structs as shown in
Listing 6-1.</p>
<figure class="listing" id="listing-6-1">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
<span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-1">Listing 6-1</a>: Storing the data and <code>IpAddrKind</code> variant of an IP address using a <code>struct</code></figcaption>
</figure>
<p>Here, weve defined a struct <code>IpAddr</code> that has two fields: a <code>kind</code> field that
is of type <code>IpAddrKind</code> (the enum we defined previously) and an <code>address</code> field
of type <code>String</code>. We have two instances of this struct. The first is <code>home</code>,
and it has the value <code>IpAddrKind::V4</code> as its <code>kind</code> with associated address
data of <code>127.0.0.1</code>. The second instance is <code>loopback</code>. It has the other
variant of <code>IpAddrKind</code> as its <code>kind</code> value, <code>V6</code>, and has address <code>::1</code>
associated with it. Weve used a struct to bundle the <code>kind</code> and <code>address</code>
values together, so now the variant is associated with the value.</p>
<p>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
variant. This new definition of the <code>IpAddr</code> enum says that both <code>V4</code> and <code>V6</code>
variants will have associated <code>String</code> values:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
<span class="boring">}</span></code></pre>
<p>We attach data to each variant of the enum directly, so there is no need for an
extra struct. Here, its also easier to see another detail of how enums work:
The name of each enum variant that we define also becomes a function that
constructs an instance of the enum. That is, <code>IpAddr::V4()</code> is a function call
that takes a <code>String</code> argument and returns an instance of the <code>IpAddr</code> type. We
automatically get this constructor function defined as a result of defining the
enum.</p>
<p>Theres another advantage to using an enum rather than a struct: Each variant
can have different types and amounts of associated data. Version four IP
addresses will always have four numeric components that will have values
between 0 and 255. If we wanted to store <code>V4</code> addresses as four <code>u8</code> values but
still express <code>V6</code> addresses as one <code>String</code> value, we wouldnt be able to with
a struct. Enums handle this case with ease:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
<span class="boring">}</span></code></pre>
<p>Weve 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 <a href="../std/net/enum.IpAddr.html">the standard
library has a definition we can use!</a><!-- ignore --> Lets look at how
the standard library defines <code>IpAddr</code>. It has the exact enum and variants that
weve 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:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>struct Ipv4Addr {
// --snip--
}
struct Ipv6Addr {
// --snip--
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
<span class="boring">}</span></code></pre>
<p>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.</p>
<p>Note that even though the standard library contains a definition for <code>IpAddr</code>,
we can still create and use our own definition without conflict because we
havent brought the standard librarys definition into our scope. Well talk
more about bringing types into scope in Chapter 7.</p>
<p>Lets look at another example of an enum in Listing 6-2: This one has a wide
variety of types embedded in its variants.</p>
<figure class="listing" id="listing-6-2">
<pre class="playground"><code class="language-rust edition2024">enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<figcaption><a href="#listing-6-2">Listing 6-2</a>: A <code>Message</code> enum whose variants each store different amounts and types of values</figcaption>
</figure>
<p>This enum has four variants with different types:</p>
<ul>
<li><code>Quit</code>: Has no data associated with it at all</li>
<li><code>Move</code>: Has named fields, like a struct does</li>
<li><code>Write</code>: Includes a single <code>String</code></li>
<li><code>ChangeColor</code>: Includes three <code>i32</code> values</li>
</ul>
<p>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 doesnt use the
<code>struct</code> keyword and all the variants are grouped together under the <code>Message</code>
type. The following structs could hold the same data that the preceding enum
variants hold:</p>
<pre class="playground"><code class="language-rust edition2024">struct QuitMessage; // unit struct
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<p>But if we used the different structs, each of which has its own type, we
couldnt as easily define a function to take any of these kinds of messages as
we could with the <code>Message</code> enum defined in Listing 6-2, which is a single type.</p>
<p>There is one more similarity between enums and structs: Just as were able to
define methods on structs using <code>impl</code>, were also able to define methods on
enums. Heres a method named <code>call</code> that we could define on our <code>Message</code> enum:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span><span class="boring"> enum Message {
</span><span class="boring"> Quit,
</span><span class="boring"> Move { x: i32, y: i32 },
</span><span class="boring"> Write(String),
</span><span class="boring"> ChangeColor(i32, i32, i32),
</span><span class="boring"> }
</span><span class="boring">
</span> impl Message {
fn call(&amp;self) {
// method body would be defined here
}
}
let m = Message::Write(String::from("hello"));
m.call();
<span class="boring">}</span></code></pre>
<p>The body of the method would use <code>self</code> to get the value that we called the
method on. In this example, weve created a variable <code>m</code> that has the value
<code>Message::Write(String::from("hello"))</code>, and that is what <code>self</code> will be in the
body of the <code>call</code> method when <code>m.call()</code> runs.</p>
<p>Lets look at another enum in the standard library that is very common and
useful: <code>Option</code>.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="the-option-enum-and-its-advantages-over-null-values"></a></p>
<h3 id="the-option-enum"><a class="header" href="#the-option-enum">The <code>Option</code> Enum</a></h3>
<p>This section explores a case study of <code>Option</code>, which is another enum defined
by the standard library. The <code>Option</code> type encodes the very common scenario in
which a value could be something, or it could be nothing.</p>
<p>For example, if you request the first item in a non-empty list, 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 youve handled all the cases you should be handling; this
functionality can prevent bugs that are extremely common in other programming
languages.</p>
<p>Programming language design is often thought of in terms of which features you
include, but the features you exclude are important too. Rust doesnt have the
null feature that many other languages have. <em>Null</em> 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.</p>
<p>In his 2009 presentation “Null References: The Billion Dollar Mistake,” Tony
Hoare, the inventor of null, had this to say:</p>
<blockquote>
<p>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 couldnt 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.</p>
</blockquote>
<p>The problem with null values is that if you try to use a null value as a
not-null value, youll get an error of some kind. Because this null or not-null
property is pervasive, its extremely easy to make this kind of error.</p>
<p>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.</p>
<p>The problem isnt 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
<code>Option&lt;T&gt;</code>, and it is <a href="../std/option/enum.Option.html">defined by the standard library</a><!-- ignore -->
as follows:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>enum Option&lt;T&gt; {
None,
Some(T),
}
<span class="boring">}</span></code></pre>
<p>The <code>Option&lt;T&gt;</code> enum is so useful that its even included in the prelude; you
dont need to bring it into scope explicitly. Its variants are also included in
the prelude: You can use <code>Some</code> and <code>None</code> directly without the <code>Option::</code>
prefix. The <code>Option&lt;T&gt;</code> enum is still just a regular enum, and <code>Some(T)</code> and
<code>None</code> are still variants of type <code>Option&lt;T&gt;</code>.</p>
<p>The <code>&lt;T&gt;</code> syntax is a feature of Rust we havent talked about yet. Its a
generic type parameter, and well cover generics in more detail in Chapter 10.
For now, all you need to know is that <code>&lt;T&gt;</code> means that the <code>Some</code> variant of
the <code>Option</code> enum can hold one piece of data of any type, and that each
concrete type that gets used in place of <code>T</code> makes the overall <code>Option&lt;T&gt;</code> type
a different type. Here are some examples of using <code>Option</code> values to hold
number types and char types:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option&lt;i32&gt; = None;
<span class="boring">}</span></code></pre>
<p>The type of <code>some_number</code> is <code>Option&lt;i32&gt;</code>. The type of <code>some_char</code> is
<code>Option&lt;char&gt;</code>, which is a different type. Rust can infer these types because
weve specified a value inside the <code>Some</code> variant. For <code>absent_number</code>, Rust
requires us to annotate the overall <code>Option</code> type: The compiler cant infer the
type that the corresponding <code>Some</code> variant will hold by looking only at a
<code>None</code> value. Here, we tell Rust that we mean for <code>absent_number</code> to be of type
<code>Option&lt;i32&gt;</code>.</p>
<p>When we have a <code>Some</code> value, we know that a value is present, and the value is
held within the <code>Some</code>. When we have a <code>None</code> value, in some sense it means the
same thing as null: We dont have a valid value. So, why is having <code>Option&lt;T&gt;</code>
any better than having null?</p>
<p>In short, because <code>Option&lt;T&gt;</code> and <code>T</code> (where <code>T</code> can be any type) are different
types, the compiler wont let us use an <code>Option&lt;T&gt;</code> value as if it were
definitely a valid value. For example, this code wont compile, because its
trying to add an <code>i8</code> to an <code>Option&lt;i8&gt;</code>:</p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
</span> let x: i8 = 5;
let y: Option&lt;i8&gt; = Some(5);
let sum = x + y;
<span class="boring">}</span></code></pre>
<p>If we run this code, we get an error message like this one:</p>
<pre><code class="language-console">$ cargo run
Compiling enums v0.1.0 (file:///projects/enums)
error[E0277]: cannot add `Option&lt;i8&gt;` to `i8`
--&gt; src/main.rs:5:17
|
5 | let sum = x + y;
| ^ no implementation for `i8 + Option&lt;i8&gt;`
|
= help: the trait `Add&lt;Option&lt;i8&gt;&gt;` is not implemented for `i8`
= help: the following other types implement trait `Add&lt;Rhs&gt;`:
`&amp;i8` implements `Add&lt;i8&gt;`
`&amp;i8` implements `Add`
`i8` implements `Add&lt;&amp;i8&gt;`
`i8` implements `Add`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `enums` (bin "enums") due to 1 previous error
</code></pre>
<p>Intense! In effect, this error message means that Rust doesnt understand how
to add an <code>i8</code> and an <code>Option&lt;i8&gt;</code>, because theyre different types. When we
have a value of a type like <code>i8</code> 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 <code>Option&lt;i8&gt;</code> (or
whatever type of value were 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.</p>
<p>In other words, you have to convert an <code>Option&lt;T&gt;</code> to a <code>T</code> before you can
perform <code>T</code> operations with it. Generally, this helps catch one of the most
common issues with null: assuming that something isnt null when it actually is.</p>
<p>Eliminating the risk of incorrectly assuming a not-null value helps you be 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 <code>Option&lt;T&gt;</code>. 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 isnt an <code>Option&lt;T&gt;</code>,
you <em>can</em> safely assume that the value isnt null. This was a deliberate design
decision for Rust to limit nulls pervasiveness and increase the safety of Rust
code.</p>
<p>So how do you get the <code>T</code> value out of a <code>Some</code> variant when you have a value
of type <code>Option&lt;T&gt;</code> so that you can use that value? The <code>Option&lt;T&gt;</code> enum has a
large number of methods that are useful in a variety of situations; you can
check them out in <a href="../std/option/enum.Option.html">its documentation</a><!-- ignore -->. Becoming familiar
with the methods on <code>Option&lt;T&gt;</code> will be extremely useful in your journey with
Rust.</p>
<p>In general, in order to use an <code>Option&lt;T&gt;</code> value, you want to have code that
will handle each variant. You want some code that will run only when you have a
<code>Some(T)</code> value, and this code is allowed to use the inner <code>T</code>. You want some
other code to run only if you have a <code>None</code> value, and that code doesnt have a
<code>T</code> value available. The <code>match</code> 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.</p>
</body>
</html>

371
ch06/ch06-02-match.html Normal file
View File

@@ -0,0 +1,371 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>The match Control Flow Construct</title>
</head>
<body>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="the-match-control-flow-operator"></a></p>
<h2 id="the-match-control-flow-construct"><a class="header" href="#the-match-control-flow-construct">The <code>match</code> Control Flow Construct</a></h2>
<p>Rust has an extremely powerful control flow construct called <code>match</code> 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; <a href="../ch19/ch19-00-patterns.html">Chapter
19</a><!-- ignore --> covers all the different kinds of patterns
and what they do. The power of <code>match</code> comes from the expressiveness of the
patterns and the fact that the compiler confirms that all possible cases are
handled.</p>
<p>Think of a <code>match</code> 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 <code>match</code>, and at the first pattern the value “fits,”
the value falls into the associated code block to be used during execution.</p>
<p>Speaking of coins, lets use them as an example using <code>match</code>! We can write a
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.</p>
<figure class="listing" id="listing-6-3">
<pre class="playground"><code class="language-rust edition2024">enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -&gt; u8 {
match coin {
Coin::Penny =&gt; 1,
Coin::Nickel =&gt; 5,
Coin::Dime =&gt; 10,
Coin::Quarter =&gt; 25,
}
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<figcaption><a href="#listing-6-3">Listing 6-3</a>: An enum and a <code>match</code> expression that has the variants of the enum as its patterns</figcaption>
</figure>
<p>Lets break down the <code>match</code> in the <code>value_in_cents</code> function. First, we list
the <code>match</code> keyword followed by an expression, which in this case is the value
<code>coin</code>. This seems very similar to a conditional expression used with <code>if</code>, but
theres a big difference: With <code>if</code>, the condition needs to evaluate to a
Boolean value, but here it can be any type. The type of <code>coin</code> in this example
is the <code>Coin</code> enum that we defined on the first line.</p>
<p>Next are the <code>match</code> arms. An arm has two parts: a pattern and some code. The
first arm here has a pattern that is the value <code>Coin::Penny</code> and then the <code>=&gt;</code>
operator that separates the pattern and the code to run. The code in this case
is just the value <code>1</code>. Each arm is separated from the next with a comma.</p>
<p>When the <code>match</code> expression executes, it compares the resultant value against
the pattern of each arm, in order. If a pattern matches the value, the code
associated with that pattern is executed. If that pattern doesnt 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 <code>match</code> has four arms.</p>
<p>The code associated with each arm is an expression, and the resultant value of
the expression in the matching arm is the value that gets returned for the
entire <code>match</code> expression.</p>
<p>We dont typically use curly brackets if the match arm code is short, as it is
in Listing 6-3 where each arm just returns a value. If you want to run multiple
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 <code>Coin::Penny</code>, but it
still returns the last value of the block, <code>1</code>:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter,
</span><span class="boring">}
</span><span class="boring">
</span>fn value_in_cents(coin: Coin) -&gt; u8 {
match coin {
Coin::Penny =&gt; {
println!("Lucky penny!");
1
}
Coin::Nickel =&gt; 5,
Coin::Dime =&gt; 10,
Coin::Quarter =&gt; 25,
}
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<h3 id="patterns-that-bind-to-values"><a class="header" href="#patterns-that-bind-to-values">Patterns That Bind to Values</a></h3>
<p>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.</p>
<p>As an example, lets 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
our <code>enum</code> by changing the <code>Quarter</code> variant to include a <code>UsState</code> value
stored inside it, which weve done in Listing 6-4.</p>
<figure class="listing" id="listing-6-4">
<pre class="playground"><code class="language-rust edition2024">#[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
Alabama,
Alaska,
// --snip--
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
<span class="boring">
</span><span class="boring">fn main() {}</span></code></pre>
<figcaption><a href="#listing-6-4">Listing 6-4</a>: A <code>Coin</code> enum in which the <code>Quarter</code> variant also holds a <code>UsState</code> value</figcaption>
</figure>
<p>Lets imagine that a friend is trying to collect all 50 state quarters. While
we sort our loose change by coin type, well also call out the name of the
state associated with each quarter so that if its one our friend doesnt have,
they can add it to their collection.</p>
<p>In the match expression for this code, we add a variable called <code>state</code> to the
pattern that matches values of the variant <code>Coin::Quarter</code>. When a
<code>Coin::Quarter</code> matches, the <code>state</code> variable will bind to the value of that
quarters state. Then, we can use <code>state</code> in the code for that arm, like so:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)]
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span>fn value_in_cents(coin: Coin) -&gt; u8 {
match coin {
Coin::Penny =&gt; 1,
Coin::Nickel =&gt; 5,
Coin::Dime =&gt; 10,
Coin::Quarter(state) =&gt; {
println!("State quarter from {state:?}!");
25
}
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> value_in_cents(Coin::Quarter(UsState::Alaska));
</span><span class="boring">}</span></code></pre>
<p>If we were to call <code>value_in_cents(Coin::Quarter(UsState::Alaska))</code>, <code>coin</code>
would be <code>Coin::Quarter(UsState::Alaska)</code>. When we compare that value with each
of the match arms, none of them match until we reach <code>Coin::Quarter(state)</code>. At
that point, the binding for <code>state</code> will be the value <code>UsState::Alaska</code>. We can
then use that binding in the <code>println!</code> expression, thus getting the inner
state value out of the <code>Coin</code> enum variant for <code>Quarter</code>.</p>
<!-- Old headings. Do not remove or links may break. -->
<p><a id="matching-with-optiont"></a></p>
<h3 id="the-optiont-match-pattern"><a class="header" href="#the-optiont-match-pattern">The <code>Option&lt;T&gt;</code> <code>match</code> Pattern</a></h3>
<p>In the previous section, we wanted to get the inner <code>T</code> value out of the <code>Some</code>
case when using <code>Option&lt;T&gt;</code>; we can also handle <code>Option&lt;T&gt;</code> using <code>match</code>, as
we did with the <code>Coin</code> enum! Instead of comparing coins, well compare the
variants of <code>Option&lt;T&gt;</code>, but the way the <code>match</code> expression works remains the
same.</p>
<p>Lets say we want to write a function that takes an <code>Option&lt;i32&gt;</code> and, if
theres a value inside, adds 1 to that value. If there isnt a value inside,
the function should return the <code>None</code> value and not attempt to perform any
operations.</p>
<p>This function is very easy to write, thanks to <code>match</code>, and will look like
Listing 6-5.</p>
<figure class="listing" id="listing-6-5">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> fn plus_one(x: Option&lt;i32&gt;) -&gt; Option&lt;i32&gt; {
match x {
None =&gt; None,
Some(i) =&gt; Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
<span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-5">Listing 6-5</a>: A function that uses a <code>match</code> expression on an <code>Option&lt;i32&gt;</code></figcaption>
</figure>
<p>Lets examine the first execution of <code>plus_one</code> in more detail. When we call
<code>plus_one(five)</code>, the variable <code>x</code> in the body of <code>plus_one</code> will have the
value <code>Some(5)</code>. We then compare that against each match arm:</p>
<pre><code class="language-rust ignore"><span class="boring">fn main() {
</span><span class="boring"> fn plus_one(x: Option&lt;i32&gt;) -&gt; Option&lt;i32&gt; {
</span><span class="boring"> match x {
</span> None =&gt; None,
<span class="boring"> Some(i) =&gt; Some(i + 1),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let five = Some(5);
</span><span class="boring"> let six = plus_one(five);
</span><span class="boring"> let none = plus_one(None);
</span><span class="boring">}</span></code></pre>
<p>The <code>Some(5)</code> value doesnt match the pattern <code>None</code>, so we continue to the
next arm:</p>
<pre><code class="language-rust ignore"><span class="boring">fn main() {
</span><span class="boring"> fn plus_one(x: Option&lt;i32&gt;) -&gt; Option&lt;i32&gt; {
</span><span class="boring"> match x {
</span><span class="boring"> None =&gt; None,
</span> Some(i) =&gt; Some(i + 1),
<span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let five = Some(5);
</span><span class="boring"> let six = plus_one(five);
</span><span class="boring"> let none = plus_one(None);
</span><span class="boring">}</span></code></pre>
<p>Does <code>Some(5)</code> match <code>Some(i)</code>? It does! We have the same variant. The <code>i</code>
binds to the value contained in <code>Some</code>, so <code>i</code> takes the value <code>5</code>. The code in
the match arm is then executed, so we add 1 to the value of <code>i</code> and create a
new <code>Some</code> value with our total <code>6</code> inside.</p>
<p>Now lets consider the second call of <code>plus_one</code> in Listing 6-5, where <code>x</code> is
<code>None</code>. We enter the <code>match</code> and compare to the first arm:</p>
<pre><code class="language-rust ignore"><span class="boring">fn main() {
</span><span class="boring"> fn plus_one(x: Option&lt;i32&gt;) -&gt; Option&lt;i32&gt; {
</span><span class="boring"> match x {
</span> None =&gt; None,
<span class="boring"> Some(i) =&gt; Some(i + 1),
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">
</span><span class="boring"> let five = Some(5);
</span><span class="boring"> let six = plus_one(five);
</span><span class="boring"> let none = plus_one(None);
</span><span class="boring">}</span></code></pre>
<p>It matches! Theres no value to add to, so the program stops and returns the
<code>None</code> value on the right side of <code>=&gt;</code>. Because the first arm matched, no other
arms are compared.</p>
<p>Combining <code>match</code> and enums is useful in many situations. Youll see this
pattern a lot in Rust code: <code>match</code> against an enum, bind a variable to the
data inside, and then execute code based on it. Its a bit tricky at first, but
once you get used to it, youll wish you had it in all languages. Its
consistently a user favorite.</p>
<h3 id="matches-are-exhaustive"><a class="header" href="#matches-are-exhaustive">Matches Are Exhaustive</a></h3>
<p>Theres one other aspect of <code>match</code> we need to discuss: The arms patterns must
cover all possibilities. Consider this version of our <code>plus_one</code> function,
which has a bug and wont compile:</p>
<pre><code class="language-rust ignore does_not_compile"><span class="boring">fn main() {
</span> fn plus_one(x: Option&lt;i32&gt;) -&gt; Option&lt;i32&gt; {
match x {
Some(i) =&gt; Some(i + 1),
}
}
<span class="boring">
</span><span class="boring"> let five = Some(5);
</span><span class="boring"> let six = plus_one(five);
</span><span class="boring"> let none = plus_one(None);
</span><span class="boring">}</span></code></pre>
<p>We didnt handle the <code>None</code> case, so this code will cause a bug. Luckily, its
a bug Rust knows how to catch. If we try to compile this code, well get this
error:</p>
<pre><code class="language-console">$ cargo run
Compiling enums v0.1.0 (file:///projects/enums)
error[E0004]: non-exhaustive patterns: `None` not covered
--&gt; src/main.rs:3:15
|
3 | match x {
| ^ pattern `None` not covered
|
note: `Option&lt;i32&gt;` defined here
--&gt; /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/option.rs:593:1
::: /rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/option.rs:597:5
|
= note: not covered
= note: the matched value is of type `Option&lt;i32&gt;`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
4 ~ Some(i) =&gt; Some(i + 1),
5 ~ None =&gt; todo!(),
|
For more information about this error, try `rustc --explain E0004`.
error: could not compile `enums` (bin "enums") due to 1 previous error
</code></pre>
<p>Rust knows that we didnt cover every possible case and even knows which
pattern we forgot! Matches in Rust are <em>exhaustive</em>: We must exhaust every last
possibility in order for the code to be valid. Especially in the case of
<code>Option&lt;T&gt;</code>, when Rust prevents us from forgetting to explicitly handle the
<code>None</code> 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.</p>
<h3 id="catch-all-patterns-and-the-_-placeholder"><a class="header" href="#catch-all-patterns-and-the-_-placeholder">Catch-All Patterns and the <code>_</code> Placeholder</a></h3>
<p>Using enums, we can also take special actions for a few particular values, but
for all other values take one default action. Imagine were implementing a game
where, if you roll a 3 on a dice roll, your player doesnt move but instead
gets a fancy new 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. Heres
a <code>match</code> 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:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let dice_roll = 9;
match dice_roll {
3 =&gt; add_fancy_hat(),
7 =&gt; remove_fancy_hat(),
other =&gt; move_player(other),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
<span class="boring">}</span></code></pre>
<p>For the first two arms, the patterns are the literal values <code>3</code> and <code>7</code>. For
the last arm that covers every other possible value, the pattern is the
variable weve chosen to name <code>other</code>. The code that runs for the <code>other</code> arm
uses the variable by passing it to the <code>move_player</code> function.</p>
<p>This code compiles, even though we havent listed all the possible values a
<code>u8</code> can have, because the last pattern will match all values not specifically
listed. This catch-all pattern meets the requirement that <code>match</code> must be
exhaustive. Note that we have to put the catch-all arm last because the
patterns are evaluated in order. If we had 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!</p>
<p>Rust also has a pattern we can use when we want a catch-all but dont want to
<em>use</em> the value in the catch-all pattern: <code>_</code> is a special pattern that matches
any value and does not bind to that value. This tells Rust we arent going to
use the value, so Rust wont warn us about an unused variable.</p>
<p>Lets 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 <code>_</code> instead of the variable named <code>other</code>:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let dice_roll = 9;
match dice_roll {
3 =&gt; add_fancy_hat(),
7 =&gt; remove_fancy_hat(),
_ =&gt; reroll(),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
<span class="boring">}</span></code></pre>
<p>This example also meets the exhaustiveness requirement because were explicitly
ignoring all other values in the last arm; we havent forgotten anything.</p>
<p>Finally, well change the rules of the game one more time so that nothing else
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 <a href="../ch03/ch03-02-data-types.html#the-tuple-type">“The Tuple
Type”</a><!-- ignore --> section) as the code that goes with the <code>_</code> arm:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let dice_roll = 9;
match dice_roll {
3 =&gt; add_fancy_hat(),
7 =&gt; remove_fancy_hat(),
_ =&gt; (),
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
<span class="boring">}</span></code></pre>
<p>Here, were telling Rust explicitly that we arent going to use any other value
that doesnt match a pattern in an earlier arm, and we dont want to run any
code in this case.</p>
<p>Theres more about patterns and matching that well cover in <a href="../ch19/ch19-00-patterns.html">Chapter
19</a><!-- ignore -->. For now, were going to move on to the
<code>if let</code> syntax, which can be useful in situations where the <code>match</code> expression
is a bit wordy.</p>
</body>
</html>

319
ch06/ch06-03-if-let.html Normal file
View File

@@ -0,0 +1,319 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Concise Control Flow with if let and let...else</title>
</head>
<body>
<h2 id="concise-control-flow-with-if-let-and-letelse"><a class="header" href="#concise-control-flow-with-if-let-and-letelse">Concise Control Flow with <code>if let</code> and <code>let...else</code></a></h2>
<p>The <code>if let</code> syntax lets you combine <code>if</code> and <code>let</code> into a less verbose way to
handle values that match one pattern while ignoring the rest. Consider the
program in Listing 6-6 that matches on an <code>Option&lt;u8&gt;</code> value in the
<code>config_max</code> variable but only wants to execute code if the value is the <code>Some</code>
variant.</p>
<figure class="listing" id="listing-6-6">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let config_max = Some(3u8);
match config_max {
Some(max) =&gt; println!("The maximum is configured to be {max}"),
_ =&gt; (),
}
<span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-6">Listing 6-6</a>: A <code>match</code> that only cares about executing code when the value is <code>Some</code></figcaption>
</figure>
<p>If the value is <code>Some</code>, we print out the value in the <code>Some</code> variant by binding
the value to the variable <code>max</code> in the pattern. We dont want to do anything
with the <code>None</code> value. To satisfy the <code>match</code> expression, we have to add <code>_ =&gt; ()</code> after processing just one variant, which is annoying boilerplate code to
add.</p>
<p>Instead, we could write this in a shorter way using <code>if let</code>. The following
code behaves the same as the <code>match</code> in Listing 6-6:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
</span> let config_max = Some(3u8);
if let Some(max) = config_max {
println!("The maximum is configured to be {max}");
}
<span class="boring">}</span></code></pre>
<p>The syntax <code>if let</code> takes a pattern and an expression separated by an equal
sign. It works the same way as a <code>match</code>, where the expression is given to the
<code>match</code> and the pattern is its first arm. In this case, the pattern is
<code>Some(max)</code>, and the <code>max</code> binds to the value inside the <code>Some</code>. We can then
use <code>max</code> in the body of the <code>if let</code> block in the same way we used <code>max</code> in
the corresponding <code>match</code> arm. The code in the <code>if let</code> block only runs if the
value matches the pattern.</p>
<p>Using <code>if let</code> means less typing, less indentation, and less boilerplate code.
However, you lose the exhaustive checking <code>match</code> enforces that ensures that
you arent forgetting to handle any cases. Choosing between <code>match</code> and <code>if let</code> depends on what youre doing in your particular situation and whether
gaining conciseness is an appropriate trade-off for losing exhaustive checking.</p>
<p>In other words, you can think of <code>if let</code> as syntax sugar for a <code>match</code> that
runs code when the value matches one pattern and then ignores all other values.</p>
<p>We can include an <code>else</code> with an <code>if let</code>. The block of code that goes with the
<code>else</code> is the same as the block of code that would go with the <code>_</code> case in the
<code>match</code> expression that is equivalent to the <code>if let</code> and <code>else</code>. Recall the
<code>Coin</code> enum definition in Listing 6-4, where the <code>Quarter</code> variant also held a
<code>UsState</code> 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 <code>match</code>
expression, like this:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)]
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let coin = Coin::Penny;
</span> let mut count = 0;
match coin {
Coin::Quarter(state) =&gt; println!("State quarter from {state:?}!"),
_ =&gt; count += 1,
}
<span class="boring">}</span></code></pre>
<p>Or we could use an <code>if let</code> and <code>else</code> expression, like this:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)]
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let coin = Coin::Penny;
</span> let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {state:?}!");
} else {
count += 1;
}
<span class="boring">}</span></code></pre>
<h2 id="staying-on-the-happy-path-with-letelse"><a class="header" href="#staying-on-the-happy-path-with-letelse">Staying on the “Happy Path” with <code>let...else</code></a></h2>
<p>The common pattern is to perform some computation when a value is present and
return a default value otherwise. Continuing with our example of coins with a
<code>UsState</code> value, if we wanted to say something funny depending on how old the
state on the quarter was, we might introduce a method on <code>UsState</code> to check the
age of a state, like so:</p>
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)] // so we can inspect the state in a minute
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span>impl UsState {
fn existed_in(&amp;self, year: u16) -&gt; bool {
match self {
UsState::Alabama =&gt; year &gt;= 1819,
UsState::Alaska =&gt; year &gt;= 1959,
// -- snip --
}
}
}
<span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn describe_state_quarter(coin: Coin) -&gt; Option&lt;String&gt; {
</span><span class="boring"> if let Coin::Quarter(state) = coin {
</span><span class="boring"> if state.existed_in(1900) {
</span><span class="boring"> Some(format!("{state:?} is pretty old, for America!"))
</span><span class="boring"> } else {
</span><span class="boring"> Some(format!("{state:?} is relatively new."))
</span><span class="boring"> }
</span><span class="boring"> } else {
</span><span class="boring"> None
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
</span><span class="boring"> println!("{desc}");
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<p>Then, we might use <code>if let</code> to match on the type of coin, introducing a <code>state</code>
variable within the body of the condition, as in Listing 6-7.</p>
<figure class="listing" id="listing-6-7">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)] // so we can inspect the state in a minute
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl UsState {
</span><span class="boring"> fn existed_in(&amp;self, year: u16) -&gt; bool {
</span><span class="boring"> match self {
</span><span class="boring"> UsState::Alabama =&gt; year &gt;= 1819,
</span><span class="boring"> UsState::Alaska =&gt; year &gt;= 1959,
</span><span class="boring"> // -- snip --
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span>fn describe_state_quarter(coin: Coin) -&gt; Option&lt;String&gt; {
if let Coin::Quarter(state) = coin {
if state.existed_in(1900) {
Some(format!("{state:?} is pretty old, for America!"))
} else {
Some(format!("{state:?} is relatively new."))
}
} else {
None
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
</span><span class="boring"> println!("{desc}");
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-7">Listing 6-7</a>: Checking whether a state existed in 1900 by using conditionals nested inside an <code>if let</code></figcaption>
</figure>
<p>That gets the job done, but it has pushed the work into the body of the <code>if let</code> statement, and if the work to be done is more complicated, it might be
hard to follow exactly how the top-level branches relate. We could also take
advantage of the fact that expressions produce a value either to produce the
<code>state</code> from the <code>if let</code> or to return early, as in Listing 6-8. (You could do
something similar with a <code>match</code>, too.)</p>
<figure class="listing" id="listing-6-8">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)] // so we can inspect the state in a minute
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl UsState {
</span><span class="boring"> fn existed_in(&amp;self, year: u16) -&gt; bool {
</span><span class="boring"> match self {
</span><span class="boring"> UsState::Alabama =&gt; year &gt;= 1819,
</span><span class="boring"> UsState::Alaska =&gt; year &gt;= 1959,
</span><span class="boring"> // -- snip --
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span>fn describe_state_quarter(coin: Coin) -&gt; Option&lt;String&gt; {
let state = if let Coin::Quarter(state) = coin {
state
} else {
return None;
};
if state.existed_in(1900) {
Some(format!("{state:?} is pretty old, for America!"))
} else {
Some(format!("{state:?} is relatively new."))
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
</span><span class="boring"> println!("{desc}");
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-8">Listing 6-8</a>: Using <code>if let</code> to produce a value or return early</figcaption>
</figure>
<p>This is a bit annoying to follow in its own way, though! One branch of the <code>if let</code> produces a value, and the other one returns from the function entirely.</p>
<p>To make this common pattern nicer to express, Rust has <code>let...else</code>. The
<code>let...else</code> syntax takes a pattern on the left side and an expression on the
right, very similar to <code>if let</code>, but it does not have an <code>if</code> branch, only an
<code>else</code> branch. If the pattern matches, it will bind the value from the pattern
in the outer scope. If the pattern does <em>not</em> match, the program will flow into
the <code>else</code> arm, which must return from the function.</p>
<p>In Listing 6-9, you can see how Listing 6-8 looks when using <code>let...else</code> in
place of <code>if let</code>.</p>
<figure class="listing" id="listing-6-9">
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#[derive(Debug)] // so we can inspect the state in a minute
</span><span class="boring">enum UsState {
</span><span class="boring"> Alabama,
</span><span class="boring"> Alaska,
</span><span class="boring"> // --snip--
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl UsState {
</span><span class="boring"> fn existed_in(&amp;self, year: u16) -&gt; bool {
</span><span class="boring"> match self {
</span><span class="boring"> UsState::Alabama =&gt; year &gt;= 1819,
</span><span class="boring"> UsState::Alaska =&gt; year &gt;= 1959,
</span><span class="boring"> // -- snip --
</span><span class="boring"> }
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">enum Coin {
</span><span class="boring"> Penny,
</span><span class="boring"> Nickel,
</span><span class="boring"> Dime,
</span><span class="boring"> Quarter(UsState),
</span><span class="boring">}
</span><span class="boring">
</span>fn describe_state_quarter(coin: Coin) -&gt; Option&lt;String&gt; {
let Coin::Quarter(state) = coin else {
return None;
};
if state.existed_in(1900) {
Some(format!("{state:?} is pretty old, for America!"))
} else {
Some(format!("{state:?} is relatively new."))
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> if let Some(desc) = describe_state_quarter(Coin::Quarter(UsState::Alaska)) {
</span><span class="boring"> println!("{desc}");
</span><span class="boring"> }
</span><span class="boring">}</span></code></pre>
<figcaption><a href="#listing-6-9">Listing 6-9</a>: Using <code>let...else</code> to clarify the flow through the function</figcaption>
</figure>
<p>Notice that it stays on the “happy path” in the main body of the function this
way, without having significantly different control flow for two branches the
way the <code>if let</code> did.</p>
<p>If you have a situation in which your program has logic that is too verbose to
express using a <code>match</code>, remember that <code>if let</code> and <code>let...else</code> are in your
Rust toolbox as well.</p>
<h2 id="summary"><a class="header" href="#summary">Summary</a></h2>
<p>Weve now covered how to use enums to create custom types that can be one of a
set of enumerated values. Weve shown how the standard librarys <code>Option&lt;T&gt;</code>
type helps you use the type system to prevent errors. When enum values have
data inside them, you can use <code>match</code> or <code>if let</code> to extract and use those
values, depending on how many cases you need to handle.</p>
<p>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
compiler will make certain your functions only get values of the type each
function expects.</p>
<p>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, lets now turn to
Rusts modules.</p>
</body>
</html>