291 lines
15 KiB
HTML
291 lines
15 KiB
HTML
<!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>
|