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

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>