]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## Processing a Series of Items with Iterators |
2 | ||
3 | The iterator pattern allows you to perform some task on a sequence of items in | |
4 | turn. An iterator is responsible for the logic of iterating over each item and | |
5 | determining when the sequence has finished. When you use iterators, you don’t | |
6 | have to reimplement that logic yourself. | |
7 | ||
8 | In Rust, iterators are *lazy*, meaning they have no effect until you call | |
9 | methods that consume the iterator to use it up. For example, the code in | |
04454e1e | 10 | Listing 13-10 creates an iterator over the items in the vector `v1` by calling |
9fa01778 | 11 | the `iter` method defined on `Vec<T>`. This code by itself doesn’t do anything |
13cf67c4 XL |
12 | useful. |
13 | ||
14 | ```rust | |
04454e1e | 15 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-10/src/main.rs:here}} |
13cf67c4 XL |
16 | ``` |
17 | ||
04454e1e | 18 | <span class="caption">Listing 13-10: Creating an iterator</span> |
13cf67c4 | 19 | |
923072b8 FG |
20 | The iterator is stored in the `v1_iter` variable. Once we’ve created an |
21 | iterator, we can use it in a variety of ways. In Listing 3-5 in Chapter 3, we | |
22 | iterated over an array using a `for` loop to execute some code on each of its | |
23 | items. Under the hood this implicitly created and then consumed an iterator, | |
24 | but we glossed over how exactly that works until now. | |
13cf67c4 | 25 | |
923072b8 FG |
26 | In the example in Listing 13-11, we separate the creation of the iterator from |
27 | the use of the iterator in the `for` loop. When the `for` loop is called using | |
28 | the iterator in `v1_iter`, each element in the iterator is used in one | |
29 | iteration of the loop, which prints out each value. | |
13cf67c4 XL |
30 | |
31 | ```rust | |
04454e1e | 32 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-11/src/main.rs:here}} |
13cf67c4 XL |
33 | ``` |
34 | ||
04454e1e | 35 | <span class="caption">Listing 13-11: Using an iterator in a `for` loop</span> |
13cf67c4 XL |
36 | |
37 | In languages that don’t have iterators provided by their standard libraries, | |
38 | you would likely write this same functionality by starting a variable at index | |
39 | 0, using that variable to index into the vector to get a value, and | |
40 | incrementing the variable value in a loop until it reached the total number of | |
41 | items in the vector. | |
42 | ||
43 | Iterators handle all that logic for you, cutting down on repetitive code you | |
44 | could potentially mess up. Iterators give you more flexibility to use the same | |
45 | logic with many different kinds of sequences, not just data structures you can | |
46 | index into, like vectors. Let’s examine how iterators do that. | |
47 | ||
48 | ### The `Iterator` Trait and the `next` Method | |
49 | ||
50 | All iterators implement a trait named `Iterator` that is defined in the | |
51 | standard library. The definition of the trait looks like this: | |
52 | ||
53 | ```rust | |
9fa01778 | 54 | pub trait Iterator { |
13cf67c4 XL |
55 | type Item; |
56 | ||
57 | fn next(&mut self) -> Option<Self::Item>; | |
58 | ||
59 | // methods with default implementations elided | |
60 | } | |
61 | ``` | |
62 | ||
63 | Notice this definition uses some new syntax: `type Item` and `Self::Item`, | |
64 | which are defining an *associated type* with this trait. We’ll talk about | |
65 | associated types in depth in Chapter 19. For now, all you need to know is that | |
66 | this code says implementing the `Iterator` trait requires that you also define | |
67 | an `Item` type, and this `Item` type is used in the return type of the `next` | |
68 | method. In other words, the `Item` type will be the type returned from the | |
69 | iterator. | |
70 | ||
71 | The `Iterator` trait only requires implementors to define one method: the | |
72 | `next` method, which returns one item of the iterator at a time wrapped in | |
73 | `Some` and, when iteration is over, returns `None`. | |
74 | ||
04454e1e | 75 | We can call the `next` method on iterators directly; Listing 13-12 demonstrates |
13cf67c4 | 76 | what values are returned from repeated calls to `next` on the iterator created |
9fa01778 | 77 | from the vector. |
13cf67c4 XL |
78 | |
79 | <span class="filename">Filename: src/lib.rs</span> | |
80 | ||
fc512014 | 81 | ```rust,noplayground |
04454e1e | 82 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-12/src/lib.rs:here}} |
13cf67c4 XL |
83 | ``` |
84 | ||
04454e1e | 85 | <span class="caption">Listing 13-12: Calling the `next` method on an |
13cf67c4 XL |
86 | iterator</span> |
87 | ||
88 | Note that we needed to make `v1_iter` mutable: calling the `next` method on an | |
89 | iterator changes internal state that the iterator uses to keep track of where | |
90 | it is in the sequence. In other words, this code *consumes*, or uses up, the | |
91 | iterator. Each call to `next` eats up an item from the iterator. We didn’t need | |
92 | to make `v1_iter` mutable when we used a `for` loop because the loop took | |
93 | ownership of `v1_iter` and made it mutable behind the scenes. | |
94 | ||
95 | Also note that the values we get from the calls to `next` are immutable | |
96 | references to the values in the vector. The `iter` method produces an iterator | |
97 | over immutable references. If we want to create an iterator that takes | |
98 | ownership of `v1` and returns owned values, we can call `into_iter` instead of | |
99 | `iter`. Similarly, if we want to iterate over mutable references, we can call | |
100 | `iter_mut` instead of `iter`. | |
101 | ||
102 | ### Methods that Consume the Iterator | |
103 | ||
104 | The `Iterator` trait has a number of different methods with default | |
105 | implementations provided by the standard library; you can find out about these | |
106 | methods by looking in the standard library API documentation for the `Iterator` | |
107 | trait. Some of these methods call the `next` method in their definition, which | |
108 | is why you’re required to implement the `next` method when implementing the | |
109 | `Iterator` trait. | |
110 | ||
111 | Methods that call `next` are called *consuming adaptors*, because calling them | |
112 | uses up the iterator. One example is the `sum` method, which takes ownership of | |
113 | the iterator and iterates through the items by repeatedly calling `next`, thus | |
114 | consuming the iterator. As it iterates through, it adds each item to a running | |
04454e1e | 115 | total and returns the total when iteration is complete. Listing 13-13 has a |
13cf67c4 XL |
116 | test illustrating a use of the `sum` method: |
117 | ||
118 | <span class="filename">Filename: src/lib.rs</span> | |
119 | ||
fc512014 | 120 | ```rust,noplayground |
04454e1e | 121 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-13/src/lib.rs:here}} |
13cf67c4 XL |
122 | ``` |
123 | ||
04454e1e | 124 | <span class="caption">Listing 13-13: Calling the `sum` method to get the total |
13cf67c4 XL |
125 | of all items in the iterator</span> |
126 | ||
127 | We aren’t allowed to use `v1_iter` after the call to `sum` because `sum` takes | |
128 | ownership of the iterator we call it on. | |
129 | ||
130 | ### Methods that Produce Other Iterators | |
131 | ||
923072b8 FG |
132 | *Iterator adaptors* are methods defined on the `Iterator` trait that don’t |
133 | consume the iterator. Instead, they produce different iterators by changing | |
134 | some aspect of the original iterator. | |
13cf67c4 | 135 | |
2b03887a | 136 | Listing 13-14 shows an example of calling the iterator adaptor method `map`, |
923072b8 FG |
137 | which takes a closure to call on each item as the items are iterated through. |
138 | The `map` method returns a new iterator that produces the modified items. The | |
139 | closure here creates a new iterator in which each item from the vector will be | |
140 | incremented by 1: | |
13cf67c4 XL |
141 | |
142 | <span class="filename">Filename: src/main.rs</span> | |
143 | ||
144 | ```rust,not_desired_behavior | |
04454e1e | 145 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-14/src/main.rs:here}} |
13cf67c4 XL |
146 | ``` |
147 | ||
04454e1e | 148 | <span class="caption">Listing 13-14: Calling the iterator adaptor `map` to |
13cf67c4 XL |
149 | create a new iterator</span> |
150 | ||
923072b8 | 151 | However, this code produces a warning: |
13cf67c4 | 152 | |
f035d41b | 153 | ```console |
04454e1e | 154 | {{#include ../listings/ch13-functional-features/listing-13-14/output.txt}} |
13cf67c4 XL |
155 | ``` |
156 | ||
04454e1e | 157 | The code in Listing 13-14 doesn’t do anything; the closure we’ve specified |
13cf67c4 XL |
158 | never gets called. The warning reminds us why: iterator adaptors are lazy, and |
159 | we need to consume the iterator here. | |
160 | ||
923072b8 FG |
161 | To fix this warning and consume the iterator, we’ll use the `collect` method, |
162 | which we used in Chapter 12 with `env::args` in Listing 12-1. This method | |
163 | consumes the iterator and collects the resulting values into a collection data | |
164 | type. | |
13cf67c4 | 165 | |
04454e1e | 166 | In Listing 13-15, we collect the results of iterating over the iterator that’s |
13cf67c4 XL |
167 | returned from the call to `map` into a vector. This vector will end up |
168 | containing each item from the original vector incremented by 1. | |
169 | ||
170 | <span class="filename">Filename: src/main.rs</span> | |
171 | ||
172 | ```rust | |
04454e1e | 173 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-15/src/main.rs:here}} |
13cf67c4 XL |
174 | ``` |
175 | ||
04454e1e | 176 | <span class="caption">Listing 13-15: Calling the `map` method to create a new |
13cf67c4 XL |
177 | iterator and then calling the `collect` method to consume the new iterator and |
178 | create a vector</span> | |
179 | ||
180 | Because `map` takes a closure, we can specify any operation we want to perform | |
181 | on each item. This is a great example of how closures let you customize some | |
182 | behavior while reusing the iteration behavior that the `Iterator` trait | |
183 | provides. | |
184 | ||
923072b8 FG |
185 | You can chain multiple calls to iterator adaptors to perform complex actions in |
186 | a readable way. But because all iterators are lazy, you have to call one of the | |
187 | consuming adaptor methods to get results from calls to iterator adaptors. | |
188 | ||
13cf67c4 XL |
189 | ### Using Closures that Capture Their Environment |
190 | ||
923072b8 FG |
191 | Many iterator adapters take closures as arguments, and commonly the closures |
192 | we’ll specify as arguments to iterator adapters will be closures that capture | |
064997fb FG |
193 | their environment. |
194 | ||
195 | For this example, we’ll use the `filter` method that takes a closure. The | |
196 | closure gets an item from the iterator and returns a `bool`. If the closure | |
197 | returns `true`, the value will be included in the iteration produced by | |
198 | `filter`. If the closure returns `false`, the value won’t be included. | |
13cf67c4 | 199 | |
04454e1e | 200 | In Listing 13-16, we use `filter` with a closure that captures the `shoe_size` |
13cf67c4 XL |
201 | variable from its environment to iterate over a collection of `Shoe` struct |
202 | instances. It will return only shoes that are the specified size. | |
203 | ||
204 | <span class="filename">Filename: src/lib.rs</span> | |
205 | ||
fc512014 | 206 | ```rust,noplayground |
04454e1e | 207 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-16/src/lib.rs}} |
13cf67c4 XL |
208 | ``` |
209 | ||
04454e1e | 210 | <span class="caption">Listing 13-16: Using the `filter` method with a closure |
13cf67c4 XL |
211 | that captures `shoe_size`</span> |
212 | ||
6a06907d | 213 | The `shoes_in_size` function takes ownership of a vector of shoes and a shoe |
13cf67c4 XL |
214 | size as parameters. It returns a vector containing only shoes of the specified |
215 | size. | |
216 | ||
6a06907d | 217 | In the body of `shoes_in_size`, we call `into_iter` to create an iterator |
13cf67c4 XL |
218 | that takes ownership of the vector. Then we call `filter` to adapt that |
219 | iterator into a new iterator that only contains elements for which the closure | |
220 | returns `true`. | |
221 | ||
222 | The closure captures the `shoe_size` parameter from the environment and | |
223 | compares the value with each shoe’s size, keeping only shoes of the size | |
224 | specified. Finally, calling `collect` gathers the values returned by the | |
225 | adapted iterator into a vector that’s returned by the function. | |
226 | ||
6a06907d | 227 | The test shows that when we call `shoes_in_size`, we get back only shoes |
13cf67c4 | 228 | that have the same size as the value we specified. |