]>
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 | |
10 | Listing 13-13 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 | |
74b04a01 | 15 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-13/src/main.rs:here}} |
13cf67c4 XL |
16 | ``` |
17 | ||
18 | <span class="caption">Listing 13-13: Creating an iterator</span> | |
19 | ||
20 | Once we’ve created an iterator, we can use it in a variety of ways. In Listing | |
21 | 3-5 in Chapter 3, we used iterators with `for` loops to execute some code on | |
22 | each item, although we glossed over what the call to `iter` did until now. | |
23 | ||
24 | The example in Listing 13-14 separates the creation of the iterator from the | |
25 | use of the iterator in the `for` loop. The iterator is stored in the `v1_iter` | |
26 | variable, and no iteration takes place at that time. When the `for` loop is | |
27 | called using the iterator in `v1_iter`, each element in the iterator is used in | |
28 | one iteration of the loop, which prints out each value. | |
29 | ||
30 | ```rust | |
74b04a01 | 31 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-14/src/main.rs:here}} |
13cf67c4 XL |
32 | ``` |
33 | ||
34 | <span class="caption">Listing 13-14: Using an iterator in a `for` loop</span> | |
35 | ||
36 | In languages that don’t have iterators provided by their standard libraries, | |
37 | you would likely write this same functionality by starting a variable at index | |
38 | 0, using that variable to index into the vector to get a value, and | |
39 | incrementing the variable value in a loop until it reached the total number of | |
40 | items in the vector. | |
41 | ||
42 | Iterators handle all that logic for you, cutting down on repetitive code you | |
43 | could potentially mess up. Iterators give you more flexibility to use the same | |
44 | logic with many different kinds of sequences, not just data structures you can | |
45 | index into, like vectors. Let’s examine how iterators do that. | |
46 | ||
47 | ### The `Iterator` Trait and the `next` Method | |
48 | ||
49 | All iterators implement a trait named `Iterator` that is defined in the | |
50 | standard library. The definition of the trait looks like this: | |
51 | ||
52 | ```rust | |
9fa01778 | 53 | pub trait Iterator { |
13cf67c4 XL |
54 | type Item; |
55 | ||
56 | fn next(&mut self) -> Option<Self::Item>; | |
57 | ||
58 | // methods with default implementations elided | |
59 | } | |
60 | ``` | |
61 | ||
62 | Notice this definition uses some new syntax: `type Item` and `Self::Item`, | |
63 | which are defining an *associated type* with this trait. We’ll talk about | |
64 | associated types in depth in Chapter 19. For now, all you need to know is that | |
65 | this code says implementing the `Iterator` trait requires that you also define | |
66 | an `Item` type, and this `Item` type is used in the return type of the `next` | |
67 | method. In other words, the `Item` type will be the type returned from the | |
68 | iterator. | |
69 | ||
70 | The `Iterator` trait only requires implementors to define one method: the | |
71 | `next` method, which returns one item of the iterator at a time wrapped in | |
72 | `Some` and, when iteration is over, returns `None`. | |
73 | ||
74 | We can call the `next` method on iterators directly; Listing 13-15 demonstrates | |
75 | what values are returned from repeated calls to `next` on the iterator created | |
9fa01778 | 76 | from the vector. |
13cf67c4 XL |
77 | |
78 | <span class="filename">Filename: src/lib.rs</span> | |
79 | ||
fc512014 | 80 | ```rust,noplayground |
74b04a01 | 81 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-15/src/lib.rs:here}} |
13cf67c4 XL |
82 | ``` |
83 | ||
84 | <span class="caption">Listing 13-15: Calling the `next` method on an | |
85 | iterator</span> | |
86 | ||
87 | Note that we needed to make `v1_iter` mutable: calling the `next` method on an | |
88 | iterator changes internal state that the iterator uses to keep track of where | |
89 | it is in the sequence. In other words, this code *consumes*, or uses up, the | |
90 | iterator. Each call to `next` eats up an item from the iterator. We didn’t need | |
91 | to make `v1_iter` mutable when we used a `for` loop because the loop took | |
92 | ownership of `v1_iter` and made it mutable behind the scenes. | |
93 | ||
94 | Also note that the values we get from the calls to `next` are immutable | |
95 | references to the values in the vector. The `iter` method produces an iterator | |
96 | over immutable references. If we want to create an iterator that takes | |
97 | ownership of `v1` and returns owned values, we can call `into_iter` instead of | |
98 | `iter`. Similarly, if we want to iterate over mutable references, we can call | |
99 | `iter_mut` instead of `iter`. | |
100 | ||
101 | ### Methods that Consume the Iterator | |
102 | ||
103 | The `Iterator` trait has a number of different methods with default | |
104 | implementations provided by the standard library; you can find out about these | |
105 | methods by looking in the standard library API documentation for the `Iterator` | |
106 | trait. Some of these methods call the `next` method in their definition, which | |
107 | is why you’re required to implement the `next` method when implementing the | |
108 | `Iterator` trait. | |
109 | ||
110 | Methods that call `next` are called *consuming adaptors*, because calling them | |
111 | uses up the iterator. One example is the `sum` method, which takes ownership of | |
112 | the iterator and iterates through the items by repeatedly calling `next`, thus | |
113 | consuming the iterator. As it iterates through, it adds each item to a running | |
114 | total and returns the total when iteration is complete. Listing 13-16 has a | |
115 | test illustrating a use of the `sum` method: | |
116 | ||
117 | <span class="filename">Filename: src/lib.rs</span> | |
118 | ||
fc512014 | 119 | ```rust,noplayground |
74b04a01 | 120 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-16/src/lib.rs:here}} |
13cf67c4 XL |
121 | ``` |
122 | ||
123 | <span class="caption">Listing 13-16: Calling the `sum` method to get the total | |
124 | of all items in the iterator</span> | |
125 | ||
126 | We aren’t allowed to use `v1_iter` after the call to `sum` because `sum` takes | |
127 | ownership of the iterator we call it on. | |
128 | ||
129 | ### Methods that Produce Other Iterators | |
130 | ||
131 | Other methods defined on the `Iterator` trait, known as *iterator adaptors*, | |
132 | allow you to change iterators into different kinds of iterators. You can chain | |
133 | multiple calls to iterator adaptors to perform complex actions in a readable | |
134 | way. But because all iterators are lazy, you have to call one of the consuming | |
135 | adaptor methods to get results from calls to iterator adaptors. | |
136 | ||
137 | Listing 13-17 shows an example of calling the iterator adaptor method `map`, | |
138 | which takes a closure to call on each item to produce a new iterator. The | |
139 | closure here creates a new iterator in which each item from the vector has been | |
140 | incremented by 1. However, this code produces a warning: | |
141 | ||
142 | <span class="filename">Filename: src/main.rs</span> | |
143 | ||
144 | ```rust,not_desired_behavior | |
74b04a01 | 145 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-17/src/main.rs:here}} |
13cf67c4 XL |
146 | ``` |
147 | ||
148 | <span class="caption">Listing 13-17: Calling the iterator adaptor `map` to | |
149 | create a new iterator</span> | |
150 | ||
151 | The warning we get is this: | |
152 | ||
f035d41b | 153 | ```console |
74b04a01 | 154 | {{#include ../listings/ch13-functional-features/listing-13-17/output.txt}} |
13cf67c4 XL |
155 | ``` |
156 | ||
157 | The code in Listing 13-17 doesn’t do anything; the closure we’ve specified | |
158 | never gets called. The warning reminds us why: iterator adaptors are lazy, and | |
159 | we need to consume the iterator here. | |
160 | ||
161 | To fix this and consume the iterator, we’ll use the `collect` method, which we | |
162 | used in Chapter 12 with `env::args` in Listing 12-1. This method consumes the | |
163 | iterator and collects the resulting values into a collection data type. | |
164 | ||
165 | In Listing 13-18, we collect the results of iterating over the iterator that’s | |
166 | returned from the call to `map` into a vector. This vector will end up | |
167 | containing each item from the original vector incremented by 1. | |
168 | ||
169 | <span class="filename">Filename: src/main.rs</span> | |
170 | ||
171 | ```rust | |
74b04a01 | 172 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-18/src/main.rs:here}} |
13cf67c4 XL |
173 | ``` |
174 | ||
175 | <span class="caption">Listing 13-18: Calling the `map` method to create a new | |
176 | iterator and then calling the `collect` method to consume the new iterator and | |
177 | create a vector</span> | |
178 | ||
179 | Because `map` takes a closure, we can specify any operation we want to perform | |
180 | on each item. This is a great example of how closures let you customize some | |
181 | behavior while reusing the iteration behavior that the `Iterator` trait | |
182 | provides. | |
183 | ||
184 | ### Using Closures that Capture Their Environment | |
185 | ||
186 | Now that we’ve introduced iterators, we can demonstrate a common use of | |
187 | closures that capture their environment by using the `filter` iterator adaptor. | |
188 | The `filter` method on an iterator takes a closure that takes each item from | |
189 | the iterator and returns a Boolean. If the closure returns `true`, the value | |
190 | will be included in the iterator produced by `filter`. If the closure returns | |
191 | `false`, the value won’t be included in the resulting iterator. | |
192 | ||
193 | In Listing 13-19, we use `filter` with a closure that captures the `shoe_size` | |
194 | variable from its environment to iterate over a collection of `Shoe` struct | |
195 | instances. It will return only shoes that are the specified size. | |
196 | ||
197 | <span class="filename">Filename: src/lib.rs</span> | |
198 | ||
fc512014 | 199 | ```rust,noplayground |
74b04a01 | 200 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-19/src/lib.rs}} |
13cf67c4 XL |
201 | ``` |
202 | ||
203 | <span class="caption">Listing 13-19: Using the `filter` method with a closure | |
204 | that captures `shoe_size`</span> | |
205 | ||
6a06907d | 206 | The `shoes_in_size` function takes ownership of a vector of shoes and a shoe |
13cf67c4 XL |
207 | size as parameters. It returns a vector containing only shoes of the specified |
208 | size. | |
209 | ||
6a06907d | 210 | In the body of `shoes_in_size`, we call `into_iter` to create an iterator |
13cf67c4 XL |
211 | that takes ownership of the vector. Then we call `filter` to adapt that |
212 | iterator into a new iterator that only contains elements for which the closure | |
213 | returns `true`. | |
214 | ||
215 | The closure captures the `shoe_size` parameter from the environment and | |
216 | compares the value with each shoe’s size, keeping only shoes of the size | |
217 | specified. Finally, calling `collect` gathers the values returned by the | |
218 | adapted iterator into a vector that’s returned by the function. | |
219 | ||
6a06907d | 220 | The test shows that when we call `shoes_in_size`, we get back only shoes |
13cf67c4 XL |
221 | that have the same size as the value we specified. |
222 | ||
223 | ### Creating Our Own Iterators with the `Iterator` Trait | |
224 | ||
225 | We’ve shown that you can create an iterator by calling `iter`, `into_iter`, or | |
226 | `iter_mut` on a vector. You can create iterators from the other collection | |
227 | types in the standard library, such as hash map. You can also create iterators | |
228 | that do anything you want by implementing the `Iterator` trait on your own | |
229 | types. As previously mentioned, the only method you’re required to provide a | |
230 | definition for is the `next` method. Once you’ve done that, you can use all | |
231 | other methods that have default implementations provided by the `Iterator` | |
232 | trait! | |
233 | ||
234 | To demonstrate, let’s create an iterator that will only ever count from 1 to 5. | |
235 | First, we’ll create a struct to hold some values. Then we’ll make this struct | |
236 | into an iterator by implementing the `Iterator` trait and using the values in | |
237 | that implementation. | |
238 | ||
239 | Listing 13-20 has the definition of the `Counter` struct and an associated | |
240 | `new` function to create instances of `Counter`: | |
241 | ||
242 | <span class="filename">Filename: src/lib.rs</span> | |
243 | ||
fc512014 | 244 | ```rust,noplayground |
74b04a01 | 245 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-20/src/lib.rs}} |
13cf67c4 XL |
246 | ``` |
247 | ||
248 | <span class="caption">Listing 13-20: Defining the `Counter` struct and a `new` | |
249 | function that creates instances of `Counter` with an initial value of 0 for | |
250 | `count`</span> | |
251 | ||
252 | The `Counter` struct has one field named `count`. This field holds a `u32` | |
253 | value that will keep track of where we are in the process of iterating from 1 | |
254 | to 5. The `count` field is private because we want the implementation of | |
255 | `Counter` to manage its value. The `new` function enforces the behavior of | |
256 | always starting new instances with a value of 0 in the `count` field. | |
257 | ||
258 | Next, we’ll implement the `Iterator` trait for our `Counter` type by defining | |
259 | the body of the `next` method to specify what we want to happen when this | |
260 | iterator is used, as shown in Listing 13-21: | |
261 | ||
262 | <span class="filename">Filename: src/lib.rs</span> | |
263 | ||
fc512014 | 264 | ```rust,noplayground |
6a06907d | 265 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-21/src/lib.rs:here}} |
13cf67c4 XL |
266 | ``` |
267 | ||
268 | <span class="caption">Listing 13-21: Implementing the `Iterator` trait on our | |
269 | `Counter` struct</span> | |
270 | ||
271 | We set the associated `Item` type for our iterator to `u32`, meaning the | |
272 | iterator will return `u32` values. Again, don’t worry about associated types | |
273 | yet, we’ll cover them in Chapter 19. | |
274 | ||
275 | We want our iterator to add 1 to the current state, so we initialized `count` | |
74b04a01 XL |
276 | to 0 so it would return 1 first. If the value of `count` is less than 5, `next` |
277 | will increment `count` and return the current value wrapped in `Some`. Once | |
278 | `count` is 5, our iterator will stop incrementing `count` and always return | |
279 | `None`. | |
13cf67c4 XL |
280 | |
281 | #### Using Our `Counter` Iterator’s `next` Method | |
282 | ||
283 | Once we’ve implemented the `Iterator` trait, we have an iterator! Listing 13-22 | |
284 | shows a test demonstrating that we can use the iterator functionality of our | |
285 | `Counter` struct by calling the `next` method on it directly, just as we did | |
286 | with the iterator created from a vector in Listing 13-15. | |
287 | ||
288 | <span class="filename">Filename: src/lib.rs</span> | |
289 | ||
fc512014 | 290 | ```rust,noplayground |
74b04a01 | 291 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-22/src/lib.rs:here}} |
13cf67c4 XL |
292 | ``` |
293 | ||
294 | <span class="caption">Listing 13-22: Testing the functionality of the `next` | |
295 | method implementation</span> | |
296 | ||
297 | This test creates a new `Counter` instance in the `counter` variable and then | |
298 | calls `next` repeatedly, verifying that we have implemented the behavior we | |
299 | want this iterator to have: returning the values from 1 to 5. | |
300 | ||
301 | #### Using Other `Iterator` Trait Methods | |
302 | ||
303 | We implemented the `Iterator` trait by defining the `next` method, so we | |
304 | can now use any `Iterator` trait method’s default implementations as defined in | |
305 | the standard library, because they all use the `next` method’s functionality. | |
306 | ||
307 | For example, if for some reason we wanted to take the values produced by an | |
308 | instance of `Counter`, pair them with values produced by another `Counter` | |
309 | instance after skipping the first value, multiply each pair together, keep only | |
310 | those results that are divisible by 3, and add all the resulting values | |
311 | together, we could do so, as shown in the test in Listing 13-23: | |
312 | ||
313 | <span class="filename">Filename: src/lib.rs</span> | |
314 | ||
fc512014 | 315 | ```rust,noplayground |
74b04a01 | 316 | {{#rustdoc_include ../listings/ch13-functional-features/listing-13-23/src/lib.rs:here}} |
13cf67c4 XL |
317 | ``` |
318 | ||
319 | <span class="caption">Listing 13-23: Using a variety of `Iterator` trait | |
320 | methods on our `Counter` iterator</span> | |
321 | ||
322 | Note that `zip` produces only four pairs; the theoretical fifth pair `(5, | |
323 | None)` is never produced because `zip` returns `None` when either of its input | |
324 | iterators return `None`. | |
325 | ||
326 | All of these method calls are possible because we specified how the `next` | |
327 | method works, and the standard library provides default implementations for | |
328 | other methods that call `next`. |