feat: added cleanscript
This commit is contained in:
290
ch13/ch13-02-iterators.html
Normal file
290
ch13/ch13-02-iterators.html
Normal file
@@ -0,0 +1,290 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Processing a Series of Items with Iterators</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="processing-a-series-of-items-with-iterators"><a class="header" href="#processing-a-series-of-items-with-iterators">Processing a Series of Items with Iterators</a></h2>
|
||||
<p>The iterator pattern allows you to perform some task on a sequence of items in
|
||||
turn. An iterator is responsible for the logic of iterating over each item and
|
||||
determining when the sequence has finished. When you use iterators, you don’t
|
||||
have to reimplement that logic yourself.</p>
|
||||
<p>In Rust, iterators are <em>lazy</em>, meaning they have no effect until you call
|
||||
methods that consume the iterator to use it up. For example, the code in
|
||||
Listing 13-10 creates an iterator over the items in the vector <code>v1</code> by calling
|
||||
the <code>iter</code> method defined on <code>Vec<T></code>. This code by itself doesn’t do anything
|
||||
useful.</p>
|
||||
<figure class="listing" id="listing-13-10">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-10">Listing 13-10</a>: Creating an iterator</figcaption>
|
||||
</figure>
|
||||
<p>The iterator is stored in the <code>v1_iter</code> variable. Once we’ve created an
|
||||
iterator, we can use it in a variety of ways. In Listing 3-5, we iterated over
|
||||
an array using a <code>for</code> loop to execute some code on each of its items. Under
|
||||
the hood, this implicitly created and then consumed an iterator, but we glossed
|
||||
over how exactly that works until now.</p>
|
||||
<p>In the example in Listing 13-11, we separate the creation of the iterator from
|
||||
the use of the iterator in the <code>for</code> loop. When the <code>for</code> loop is called using
|
||||
the iterator in <code>v1_iter</code>, each element in the iterator is used in one
|
||||
iteration of the loop, which prints out each value.</p>
|
||||
<figure class="listing" id="listing-13-11">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
|
||||
for val in v1_iter {
|
||||
println!("Got: {val}");
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-11">Listing 13-11</a>: Using an iterator in a <code>for</code> loop</figcaption>
|
||||
</figure>
|
||||
<p>In languages that don’t have iterators provided by their standard libraries,
|
||||
you would likely write this same functionality by starting a variable at index
|
||||
0, using that variable to index into the vector to get a value, and
|
||||
incrementing the variable value in a loop until it reached the total number of
|
||||
items in the vector.</p>
|
||||
<p>Iterators handle all of that logic for you, cutting down on repetitive code you
|
||||
could potentially mess up. Iterators give you more flexibility to use the same
|
||||
logic with many different kinds of sequences, not just data structures you can
|
||||
index into, like vectors. Let’s examine how iterators do that.</p>
|
||||
<h3 id="the-iterator-trait-and-the-next-method"><a class="header" href="#the-iterator-trait-and-the-next-method">The <code>Iterator</code> Trait and the <code>next</code> Method</a></h3>
|
||||
<p>All iterators implement a trait named <code>Iterator</code> that is defined in the
|
||||
standard library. The definition of the trait looks like this:</p>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">#![allow(unused)]
|
||||
</span><span class="boring">fn main() {
|
||||
</span>pub trait Iterator {
|
||||
type Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>;
|
||||
|
||||
// methods with default implementations elided
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<p>Notice that this definition uses some new syntax: <code>type Item</code> and <code>Self::Item</code>,
|
||||
which are defining an associated type with this trait. We’ll talk about
|
||||
associated types in depth in Chapter 20. For now, all you need to know is that
|
||||
this code says implementing the <code>Iterator</code> trait requires that you also define
|
||||
an <code>Item</code> type, and this <code>Item</code> type is used in the return type of the <code>next</code>
|
||||
method. In other words, the <code>Item</code> type will be the type returned from the
|
||||
iterator.</p>
|
||||
<p>The <code>Iterator</code> trait only requires implementors to define one method: the
|
||||
<code>next</code> method, which returns one item of the iterator at a time, wrapped in
|
||||
<code>Some</code>, and, when iteration is over, returns <code>None</code>.</p>
|
||||
<p>We can call the <code>next</code> method on iterators directly; Listing 13-12 demonstrates
|
||||
what values are returned from repeated calls to <code>next</code> on the iterator created
|
||||
from the vector.</p>
|
||||
<figure class="listing" id="listing-13-12">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span> #[test]
|
||||
fn iterator_demonstration() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let mut v1_iter = v1.iter();
|
||||
|
||||
assert_eq!(v1_iter.next(), Some(&1));
|
||||
assert_eq!(v1_iter.next(), Some(&2));
|
||||
assert_eq!(v1_iter.next(), Some(&3));
|
||||
assert_eq!(v1_iter.next(), None);
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-12">Listing 13-12</a>: Calling the <code>next</code> method on an iterator</figcaption>
|
||||
</figure>
|
||||
<p>Note that we needed to make <code>v1_iter</code> mutable: Calling the <code>next</code> method on an
|
||||
iterator changes internal state that the iterator uses to keep track of where
|
||||
it is in the sequence. In other words, this code <em>consumes</em>, or uses up, the
|
||||
iterator. Each call to <code>next</code> eats up an item from the iterator. We didn’t need
|
||||
to make <code>v1_iter</code> mutable when we used a <code>for</code> loop, because the loop took
|
||||
ownership of <code>v1_iter</code> and made it mutable behind the scenes.</p>
|
||||
<p>Also note that the values we get from the calls to <code>next</code> are immutable
|
||||
references to the values in the vector. The <code>iter</code> method produces an iterator
|
||||
over immutable references. If we want to create an iterator that takes
|
||||
ownership of <code>v1</code> and returns owned values, we can call <code>into_iter</code> instead of
|
||||
<code>iter</code>. Similarly, if we want to iterate over mutable references, we can call
|
||||
<code>iter_mut</code> instead of <code>iter</code>.</p>
|
||||
<h3 id="methods-that-consume-the-iterator"><a class="header" href="#methods-that-consume-the-iterator">Methods That Consume the Iterator</a></h3>
|
||||
<p>The <code>Iterator</code> trait has a number of different methods with default
|
||||
implementations provided by the standard library; you can find out about these
|
||||
methods by looking in the standard library API documentation for the <code>Iterator</code>
|
||||
trait. Some of these methods call the <code>next</code> method in their definition, which
|
||||
is why you’re required to implement the <code>next</code> method when implementing the
|
||||
<code>Iterator</code> trait.</p>
|
||||
<p>Methods that call <code>next</code> are called <em>consuming adapters</em> because calling them
|
||||
uses up the iterator. One example is the <code>sum</code> method, which takes ownership of
|
||||
the iterator and iterates through the items by repeatedly calling <code>next</code>, thus
|
||||
consuming the iterator. As it iterates through, it adds each item to a running
|
||||
total and returns the total when iteration is complete. Listing 13-13 has a
|
||||
test illustrating a use of the <code>sum</code> method.</p>
|
||||
<figure class="listing" id="listing-13-13">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground"><span class="boring">#[cfg(test)]
|
||||
</span><span class="boring">mod tests {
|
||||
</span> #[test]
|
||||
fn iterator_sum() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
|
||||
let total: i32 = v1_iter.sum();
|
||||
|
||||
assert_eq!(total, 6);
|
||||
}
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-13">Listing 13-13</a>: Calling the <code>sum</code> method to get the total of all items in the iterator</figcaption>
|
||||
</figure>
|
||||
<p>We aren’t allowed to use <code>v1_iter</code> after the call to <code>sum</code>, because <code>sum</code> takes
|
||||
ownership of the iterator we call it on.</p>
|
||||
<h3 id="methods-that-produce-other-iterators"><a class="header" href="#methods-that-produce-other-iterators">Methods That Produce Other Iterators</a></h3>
|
||||
<p><em>Iterator adapters</em> are methods defined on the <code>Iterator</code> trait that don’t
|
||||
consume the iterator. Instead, they produce different iterators by changing
|
||||
some aspect of the original iterator.</p>
|
||||
<p>Listing 13-14 shows an example of calling the iterator adapter method <code>map</code>,
|
||||
which takes a closure to call on each item as the items are iterated through.
|
||||
The <code>map</code> method returns a new iterator that produces the modified items. The
|
||||
closure here creates a new iterator in which each item from the vector will be
|
||||
incremented by 1.</p>
|
||||
<figure class="listing" id="listing-13-14">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre class="playground"><code class="language-rust not_desired_behavior edition2024"><span class="boring">fn main() {
|
||||
</span> let v1: Vec<i32> = vec![1, 2, 3];
|
||||
|
||||
v1.iter().map(|x| x + 1);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-14">Listing 13-14</a>: Calling the iterator adapter <code>map</code> to create a new iterator</figcaption>
|
||||
</figure>
|
||||
<p>However, this code produces a warning:</p>
|
||||
<pre><code class="language-console">$ cargo run
|
||||
Compiling iterators v0.1.0 (file:///projects/iterators)
|
||||
warning: unused `Map` that must be used
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | v1.iter().map(|x| x + 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: iterators are lazy and do nothing unless consumed
|
||||
= note: `#[warn(unused_must_use)]` on by default
|
||||
help: use `let _ = ...` to ignore the resulting value
|
||||
|
|
||||
4 | let _ = v1.iter().map(|x| x + 1);
|
||||
| +++++++
|
||||
|
||||
warning: `iterators` (bin "iterators") generated 1 warning
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.47s
|
||||
Running `target/debug/iterators`
|
||||
</code></pre>
|
||||
<p>The code in Listing 13-14 doesn’t do anything; the closure we’ve specified
|
||||
never gets called. The warning reminds us why: Iterator adapters are lazy, and
|
||||
we need to consume the iterator here.</p>
|
||||
<p>To fix this warning and consume the iterator, we’ll use the <code>collect</code> method,
|
||||
which we used with <code>env::args</code> in Listing 12-1. This method consumes the
|
||||
iterator and collects the resultant values into a collection data type.</p>
|
||||
<p>In Listing 13-15, we collect the results of iterating over the iterator that’s
|
||||
returned from the call to <code>map</code> into a vector. This vector will end up
|
||||
containing each item from the original vector, incremented by 1.</p>
|
||||
<figure class="listing" id="listing-13-15">
|
||||
<span class="file-name">Filename: src/main.rs</span>
|
||||
<pre class="playground"><code class="language-rust edition2024"><span class="boring">fn main() {
|
||||
</span> let v1: Vec<i32> = vec![1, 2, 3];
|
||||
|
||||
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
|
||||
|
||||
assert_eq!(v2, vec![2, 3, 4]);
|
||||
<span class="boring">}</span></code></pre>
|
||||
<figcaption><a href="#listing-13-15">Listing 13-15</a>: Calling the <code>map</code> method to create a new iterator, and then calling the <code>collect</code> method to consume the new iterator and create a vector</figcaption>
|
||||
</figure>
|
||||
<p>Because <code>map</code> takes a closure, we can specify any operation we want to perform
|
||||
on each item. This is a great example of how closures let you customize some
|
||||
behavior while reusing the iteration behavior that the <code>Iterator</code> trait
|
||||
provides.</p>
|
||||
<p>You can chain multiple calls to iterator adapters to perform complex actions in
|
||||
a readable way. But because all iterators are lazy, you have to call one of the
|
||||
consuming adapter methods to get results from calls to iterator adapters.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="using-closures-that-capture-their-environment"></a></p>
|
||||
<h3 id="closures-that-capture-their-environment"><a class="header" href="#closures-that-capture-their-environment">Closures That Capture Their Environment</a></h3>
|
||||
<p>Many iterator adapters take closures as arguments, and commonly the closures
|
||||
we’ll specify as arguments to iterator adapters will be closures that capture
|
||||
their environment.</p>
|
||||
<p>For this example, we’ll use the <code>filter</code> method that takes a closure. The
|
||||
closure gets an item from the iterator and returns a <code>bool</code>. If the closure
|
||||
returns <code>true</code>, the value will be included in the iteration produced by
|
||||
<code>filter</code>. If the closure returns <code>false</code>, the value won’t be included.</p>
|
||||
<p>In Listing 13-16, we use <code>filter</code> with a closure that captures the <code>shoe_size</code>
|
||||
variable from its environment to iterate over a collection of <code>Shoe</code> struct
|
||||
instances. It will return only shoes that are the specified size.</p>
|
||||
<figure class="listing" id="listing-13-16">
|
||||
<span class="file-name">Filename: src/lib.rs</span>
|
||||
<pre><code class="language-rust noplayground">#[derive(PartialEq, Debug)]
|
||||
struct Shoe {
|
||||
size: u32,
|
||||
style: String,
|
||||
}
|
||||
|
||||
fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
|
||||
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn filters_by_size() {
|
||||
let shoes = vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker"),
|
||||
},
|
||||
Shoe {
|
||||
size: 13,
|
||||
style: String::from("sandal"),
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot"),
|
||||
},
|
||||
];
|
||||
|
||||
let in_my_size = shoes_in_size(shoes, 10);
|
||||
|
||||
assert_eq!(
|
||||
in_my_size,
|
||||
vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker")
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot")
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-13-16">Listing 13-16</a>: Using the <code>filter</code> method with a closure that captures <code>shoe_size</code></figcaption>
|
||||
</figure>
|
||||
<p>The <code>shoes_in_size</code> function takes ownership of a vector of shoes and a shoe
|
||||
size as parameters. It returns a vector containing only shoes of the specified
|
||||
size.</p>
|
||||
<p>In the body of <code>shoes_in_size</code>, we call <code>into_iter</code> to create an iterator that
|
||||
takes ownership of the vector. Then, we call <code>filter</code> to adapt that iterator
|
||||
into a new iterator that only contains elements for which the closure returns
|
||||
<code>true</code>.</p>
|
||||
<p>The closure captures the <code>shoe_size</code> parameter from the environment and
|
||||
compares the value with each shoe’s size, keeping only shoes of the size
|
||||
specified. Finally, calling <code>collect</code> gathers the values returned by the
|
||||
adapted iterator into a vector that’s returned by the function.</p>
|
||||
<p>The test shows that when we call <code>shoes_in_size</code>, we get back only shoes that
|
||||
have the same size as the value we specified.</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user