]>
Commit | Line | Data |
---|---|---|
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 | |
11 | the `iter` method defined on `Vec`. This code by itself doesn’t do anything | |
12 | useful. | |
13 | ||
14 | ```rust | |
15 | let v1 = vec![1, 2, 3]; | |
16 | ||
17 | let v1_iter = v1.iter(); | |
18 | ``` | |
19 | ||
20 | <span class="caption">Listing 13-13: Creating an iterator</span> | |
21 | ||
22 | Once we’ve created an iterator, we can use it in a variety of ways. In Listing | |
23 | 3-5 in Chapter 3, we used iterators with `for` loops to execute some code on | |
24 | each item, although we glossed over what the call to `iter` did until now. | |
25 | ||
26 | The example in Listing 13-14 separates the creation of the iterator from the | |
27 | use of the iterator in the `for` loop. The iterator is stored in the `v1_iter` | |
28 | variable, and no iteration takes place at that time. When the `for` loop is | |
29 | called using the iterator in `v1_iter`, each element in the iterator is used in | |
30 | one iteration of the loop, which prints out each value. | |
31 | ||
32 | ```rust | |
33 | let v1 = vec![1, 2, 3]; | |
34 | ||
35 | let v1_iter = v1.iter(); | |
36 | ||
37 | for val in v1_iter { | |
38 | println!("Got: {}", val); | |
39 | } | |
40 | ``` | |
41 | ||
42 | <span class="caption">Listing 13-14: Using an iterator in a `for` loop</span> | |
43 | ||
44 | In languages that don’t have iterators provided by their standard libraries, | |
45 | you would likely write this same functionality by starting a variable at index | |
46 | 0, using that variable to index into the vector to get a value, and | |
47 | incrementing the variable value in a loop until it reached the total number of | |
48 | items in the vector. | |
49 | ||
50 | Iterators handle all that logic for you, cutting down on repetitive code you | |
51 | could potentially mess up. Iterators give you more flexibility to use the same | |
52 | logic with many different kinds of sequences, not just data structures you can | |
53 | index into, like vectors. Let’s examine how iterators do that. | |
54 | ||
55 | ### The `Iterator` Trait and the `next` Method | |
56 | ||
57 | All iterators implement a trait named `Iterator` that is defined in the | |
58 | standard library. The definition of the trait looks like this: | |
59 | ||
60 | ```rust | |
61 | trait Iterator { | |
62 | type Item; | |
63 | ||
64 | fn next(&mut self) -> Option<Self::Item>; | |
65 | ||
66 | // methods with default implementations elided | |
67 | } | |
68 | ``` | |
69 | ||
70 | Notice this definition uses some new syntax: `type Item` and `Self::Item`, | |
71 | which are defining an *associated type* with this trait. We’ll talk about | |
72 | associated types in depth in Chapter 19. For now, all you need to know is that | |
73 | this code says implementing the `Iterator` trait requires that you also define | |
74 | an `Item` type, and this `Item` type is used in the return type of the `next` | |
75 | method. In other words, the `Item` type will be the type returned from the | |
76 | iterator. | |
77 | ||
78 | The `Iterator` trait only requires implementors to define one method: the | |
79 | `next` method, which returns one item of the iterator at a time wrapped in | |
80 | `Some` and, when iteration is over, returns `None`. | |
81 | ||
82 | We can call the `next` method on iterators directly; Listing 13-15 demonstrates | |
83 | what values are returned from repeated calls to `next` on the iterator created | |
84 | from the vector: | |
85 | ||
86 | <span class="filename">Filename: src/lib.rs</span> | |
87 | ||
88 | ```rust | |
89 | #[test] | |
90 | fn iterator_demonstration() { | |
91 | let v1 = vec![1, 2, 3]; | |
92 | ||
93 | let mut v1_iter = v1.iter(); | |
94 | ||
95 | assert_eq!(v1_iter.next(), Some(&1)); | |
96 | assert_eq!(v1_iter.next(), Some(&2)); | |
97 | assert_eq!(v1_iter.next(), Some(&3)); | |
98 | assert_eq!(v1_iter.next(), None); | |
99 | } | |
100 | ``` | |
101 | ||
102 | <span class="caption">Listing 13-15: Calling the `next` method on an | |
103 | iterator</span> | |
104 | ||
105 | Note that we needed to make `v1_iter` mutable: calling the `next` method on an | |
106 | iterator changes internal state that the iterator uses to keep track of where | |
107 | it is in the sequence. In other words, this code *consumes*, or uses up, the | |
108 | iterator. Each call to `next` eats up an item from the iterator. We didn’t need | |
109 | to make `v1_iter` mutable when we used a `for` loop because the loop took | |
110 | ownership of `v1_iter` and made it mutable behind the scenes. | |
111 | ||
112 | Also note that the values we get from the calls to `next` are immutable | |
113 | references to the values in the vector. The `iter` method produces an iterator | |
114 | over immutable references. If we want to create an iterator that takes | |
115 | ownership of `v1` and returns owned values, we can call `into_iter` instead of | |
116 | `iter`. Similarly, if we want to iterate over mutable references, we can call | |
117 | `iter_mut` instead of `iter`. | |
118 | ||
119 | ### Methods that Consume the Iterator | |
120 | ||
121 | The `Iterator` trait has a number of different methods with default | |
122 | implementations provided by the standard library; you can find out about these | |
123 | methods by looking in the standard library API documentation for the `Iterator` | |
124 | trait. Some of these methods call the `next` method in their definition, which | |
125 | is why you’re required to implement the `next` method when implementing the | |
126 | `Iterator` trait. | |
127 | ||
128 | Methods that call `next` are called *consuming adaptors*, because calling them | |
129 | uses up the iterator. One example is the `sum` method, which takes ownership of | |
130 | the iterator and iterates through the items by repeatedly calling `next`, thus | |
131 | consuming the iterator. As it iterates through, it adds each item to a running | |
132 | total and returns the total when iteration is complete. Listing 13-16 has a | |
133 | test illustrating a use of the `sum` method: | |
134 | ||
135 | <span class="filename">Filename: src/lib.rs</span> | |
136 | ||
137 | ```rust | |
138 | #[test] | |
139 | fn iterator_sum() { | |
140 | let v1 = vec![1, 2, 3]; | |
141 | ||
142 | let v1_iter = v1.iter(); | |
143 | ||
144 | let total: i32 = v1_iter.sum(); | |
145 | ||
146 | assert_eq!(total, 6); | |
147 | } | |
148 | ``` | |
149 | ||
150 | <span class="caption">Listing 13-16: Calling the `sum` method to get the total | |
151 | of all items in the iterator</span> | |
152 | ||
153 | We aren’t allowed to use `v1_iter` after the call to `sum` because `sum` takes | |
154 | ownership of the iterator we call it on. | |
155 | ||
156 | ### Methods that Produce Other Iterators | |
157 | ||
158 | Other methods defined on the `Iterator` trait, known as *iterator adaptors*, | |
159 | allow you to change iterators into different kinds of iterators. You can chain | |
160 | multiple calls to iterator adaptors to perform complex actions in a readable | |
161 | way. But because all iterators are lazy, you have to call one of the consuming | |
162 | adaptor methods to get results from calls to iterator adaptors. | |
163 | ||
164 | Listing 13-17 shows an example of calling the iterator adaptor method `map`, | |
165 | which takes a closure to call on each item to produce a new iterator. The | |
166 | closure here creates a new iterator in which each item from the vector has been | |
167 | incremented by 1. However, this code produces a warning: | |
168 | ||
169 | <span class="filename">Filename: src/main.rs</span> | |
170 | ||
171 | ```rust,not_desired_behavior | |
172 | let v1: Vec<i32> = vec![1, 2, 3]; | |
173 | ||
174 | v1.iter().map(|x| x + 1); | |
175 | ``` | |
176 | ||
177 | <span class="caption">Listing 13-17: Calling the iterator adaptor `map` to | |
178 | create a new iterator</span> | |
179 | ||
180 | The warning we get is this: | |
181 | ||
182 | ```text | |
183 | warning: unused `std::iter::Map` which must be used: iterator adaptors are lazy | |
184 | and do nothing unless consumed | |
185 | --> src/main.rs:4:5 | |
186 | | | |
187 | 4 | v1.iter().map(|x| x + 1); | |
188 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
189 | | | |
190 | = note: #[warn(unused_must_use)] on by default | |
191 | ``` | |
192 | ||
193 | The code in Listing 13-17 doesn’t do anything; the closure we’ve specified | |
194 | never gets called. The warning reminds us why: iterator adaptors are lazy, and | |
195 | we need to consume the iterator here. | |
196 | ||
197 | To fix this and consume the iterator, we’ll use the `collect` method, which we | |
198 | used in Chapter 12 with `env::args` in Listing 12-1. This method consumes the | |
199 | iterator and collects the resulting values into a collection data type. | |
200 | ||
201 | In Listing 13-18, we collect the results of iterating over the iterator that’s | |
202 | returned from the call to `map` into a vector. This vector will end up | |
203 | containing each item from the original vector incremented by 1. | |
204 | ||
205 | <span class="filename">Filename: src/main.rs</span> | |
206 | ||
207 | ```rust | |
208 | let v1: Vec<i32> = vec![1, 2, 3]; | |
209 | ||
210 | let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); | |
211 | ||
212 | assert_eq!(v2, vec![2, 3, 4]); | |
213 | ``` | |
214 | ||
215 | <span class="caption">Listing 13-18: Calling the `map` method to create a new | |
216 | iterator and then calling the `collect` method to consume the new iterator and | |
217 | create a vector</span> | |
218 | ||
219 | Because `map` takes a closure, we can specify any operation we want to perform | |
220 | on each item. This is a great example of how closures let you customize some | |
221 | behavior while reusing the iteration behavior that the `Iterator` trait | |
222 | provides. | |
223 | ||
224 | ### Using Closures that Capture Their Environment | |
225 | ||
226 | Now that we’ve introduced iterators, we can demonstrate a common use of | |
227 | closures that capture their environment by using the `filter` iterator adaptor. | |
228 | The `filter` method on an iterator takes a closure that takes each item from | |
229 | the iterator and returns a Boolean. If the closure returns `true`, the value | |
230 | will be included in the iterator produced by `filter`. If the closure returns | |
231 | `false`, the value won’t be included in the resulting iterator. | |
232 | ||
233 | In Listing 13-19, we use `filter` with a closure that captures the `shoe_size` | |
234 | variable from its environment to iterate over a collection of `Shoe` struct | |
235 | instances. It will return only shoes that are the specified size. | |
236 | ||
237 | <span class="filename">Filename: src/lib.rs</span> | |
238 | ||
239 | ```rust | |
240 | #[derive(PartialEq, Debug)] | |
241 | struct Shoe { | |
242 | size: u32, | |
243 | style: String, | |
244 | } | |
245 | ||
246 | fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> { | |
247 | shoes.into_iter() | |
248 | .filter(|s| s.size == shoe_size) | |
249 | .collect() | |
250 | } | |
251 | ||
252 | #[test] | |
253 | fn filters_by_size() { | |
254 | let shoes = vec![ | |
255 | Shoe { size: 10, style: String::from("sneaker") }, | |
256 | Shoe { size: 13, style: String::from("sandal") }, | |
257 | Shoe { size: 10, style: String::from("boot") }, | |
258 | ]; | |
259 | ||
260 | let in_my_size = shoes_in_my_size(shoes, 10); | |
261 | ||
262 | assert_eq!( | |
263 | in_my_size, | |
264 | vec![ | |
265 | Shoe { size: 10, style: String::from("sneaker") }, | |
266 | Shoe { size: 10, style: String::from("boot") }, | |
267 | ] | |
268 | ); | |
269 | } | |
270 | ``` | |
271 | ||
272 | <span class="caption">Listing 13-19: Using the `filter` method with a closure | |
273 | that captures `shoe_size`</span> | |
274 | ||
275 | The `shoes_in_my_size` function takes ownership of a vector of shoes and a shoe | |
276 | size as parameters. It returns a vector containing only shoes of the specified | |
277 | size. | |
278 | ||
279 | In the body of `shoes_in_my_size`, we call `into_iter` to create an iterator | |
280 | that takes ownership of the vector. Then we call `filter` to adapt that | |
281 | iterator into a new iterator that only contains elements for which the closure | |
282 | returns `true`. | |
283 | ||
284 | The closure captures the `shoe_size` parameter from the environment and | |
285 | compares the value with each shoe’s size, keeping only shoes of the size | |
286 | specified. Finally, calling `collect` gathers the values returned by the | |
287 | adapted iterator into a vector that’s returned by the function. | |
288 | ||
289 | The test shows that when we call `shoes_in_my_size`, we get back only shoes | |
290 | that have the same size as the value we specified. | |
291 | ||
292 | ### Creating Our Own Iterators with the `Iterator` Trait | |
293 | ||
294 | We’ve shown that you can create an iterator by calling `iter`, `into_iter`, or | |
295 | `iter_mut` on a vector. You can create iterators from the other collection | |
296 | types in the standard library, such as hash map. You can also create iterators | |
297 | that do anything you want by implementing the `Iterator` trait on your own | |
298 | types. As previously mentioned, the only method you’re required to provide a | |
299 | definition for is the `next` method. Once you’ve done that, you can use all | |
300 | other methods that have default implementations provided by the `Iterator` | |
301 | trait! | |
302 | ||
303 | To demonstrate, let’s create an iterator that will only ever count from 1 to 5. | |
304 | First, we’ll create a struct to hold some values. Then we’ll make this struct | |
305 | into an iterator by implementing the `Iterator` trait and using the values in | |
306 | that implementation. | |
307 | ||
308 | Listing 13-20 has the definition of the `Counter` struct and an associated | |
309 | `new` function to create instances of `Counter`: | |
310 | ||
311 | <span class="filename">Filename: src/lib.rs</span> | |
312 | ||
313 | ```rust | |
314 | struct Counter { | |
315 | count: u32, | |
316 | } | |
317 | ||
318 | impl Counter { | |
319 | fn new() -> Counter { | |
320 | Counter { count: 0 } | |
321 | } | |
322 | } | |
323 | ``` | |
324 | ||
325 | <span class="caption">Listing 13-20: Defining the `Counter` struct and a `new` | |
326 | function that creates instances of `Counter` with an initial value of 0 for | |
327 | `count`</span> | |
328 | ||
329 | The `Counter` struct has one field named `count`. This field holds a `u32` | |
330 | value that will keep track of where we are in the process of iterating from 1 | |
331 | to 5. The `count` field is private because we want the implementation of | |
332 | `Counter` to manage its value. The `new` function enforces the behavior of | |
333 | always starting new instances with a value of 0 in the `count` field. | |
334 | ||
335 | Next, we’ll implement the `Iterator` trait for our `Counter` type by defining | |
336 | the body of the `next` method to specify what we want to happen when this | |
337 | iterator is used, as shown in Listing 13-21: | |
338 | ||
339 | <span class="filename">Filename: src/lib.rs</span> | |
340 | ||
341 | ```rust | |
342 | # struct Counter { | |
343 | # count: u32, | |
344 | # } | |
345 | # | |
346 | impl Iterator for Counter { | |
347 | type Item = u32; | |
348 | ||
349 | fn next(&mut self) -> Option<Self::Item> { | |
350 | self.count += 1; | |
351 | ||
352 | if self.count < 6 { | |
353 | Some(self.count) | |
354 | } else { | |
355 | None | |
356 | } | |
357 | } | |
358 | } | |
359 | ``` | |
360 | ||
361 | <span class="caption">Listing 13-21: Implementing the `Iterator` trait on our | |
362 | `Counter` struct</span> | |
363 | ||
364 | We set the associated `Item` type for our iterator to `u32`, meaning the | |
365 | iterator will return `u32` values. Again, don’t worry about associated types | |
366 | yet, we’ll cover them in Chapter 19. | |
367 | ||
368 | We want our iterator to add 1 to the current state, so we initialized `count` | |
369 | to 0 so it would return 1 first. If the value of `count` is less than 6, `next` | |
370 | will return the current value wrapped in `Some`, but if `count` is 6 or higher, | |
371 | our iterator will return `None`. | |
372 | ||
373 | #### Using Our `Counter` Iterator’s `next` Method | |
374 | ||
375 | Once we’ve implemented the `Iterator` trait, we have an iterator! Listing 13-22 | |
376 | shows a test demonstrating that we can use the iterator functionality of our | |
377 | `Counter` struct by calling the `next` method on it directly, just as we did | |
378 | with the iterator created from a vector in Listing 13-15. | |
379 | ||
380 | <span class="filename">Filename: src/lib.rs</span> | |
381 | ||
382 | ```rust | |
383 | # struct Counter { | |
384 | # count: u32, | |
385 | # } | |
386 | # | |
387 | # impl Iterator for Counter { | |
388 | # type Item = u32; | |
389 | # | |
390 | # fn next(&mut self) -> Option<Self::Item> { | |
391 | # self.count += 1; | |
392 | # | |
393 | # if self.count < 6 { | |
394 | # Some(self.count) | |
395 | # } else { | |
396 | # None | |
397 | # } | |
398 | # } | |
399 | # } | |
400 | # | |
401 | #[test] | |
402 | fn calling_next_directly() { | |
403 | let mut counter = Counter::new(); | |
404 | ||
405 | assert_eq!(counter.next(), Some(1)); | |
406 | assert_eq!(counter.next(), Some(2)); | |
407 | assert_eq!(counter.next(), Some(3)); | |
408 | assert_eq!(counter.next(), Some(4)); | |
409 | assert_eq!(counter.next(), Some(5)); | |
410 | assert_eq!(counter.next(), None); | |
411 | } | |
412 | ``` | |
413 | ||
414 | <span class="caption">Listing 13-22: Testing the functionality of the `next` | |
415 | method implementation</span> | |
416 | ||
417 | This test creates a new `Counter` instance in the `counter` variable and then | |
418 | calls `next` repeatedly, verifying that we have implemented the behavior we | |
419 | want this iterator to have: returning the values from 1 to 5. | |
420 | ||
421 | #### Using Other `Iterator` Trait Methods | |
422 | ||
423 | We implemented the `Iterator` trait by defining the `next` method, so we | |
424 | can now use any `Iterator` trait method’s default implementations as defined in | |
425 | the standard library, because they all use the `next` method’s functionality. | |
426 | ||
427 | For example, if for some reason we wanted to take the values produced by an | |
428 | instance of `Counter`, pair them with values produced by another `Counter` | |
429 | instance after skipping the first value, multiply each pair together, keep only | |
430 | those results that are divisible by 3, and add all the resulting values | |
431 | together, we could do so, as shown in the test in Listing 13-23: | |
432 | ||
433 | <span class="filename">Filename: src/lib.rs</span> | |
434 | ||
435 | ```rust | |
436 | # struct Counter { | |
437 | # count: u32, | |
438 | # } | |
439 | # | |
440 | # impl Counter { | |
441 | # fn new() -> Counter { | |
442 | # Counter { count: 0 } | |
443 | # } | |
444 | # } | |
445 | # | |
446 | # impl Iterator for Counter { | |
447 | # // Our iterator will produce u32s | |
448 | # type Item = u32; | |
449 | # | |
450 | # fn next(&mut self) -> Option<Self::Item> { | |
451 | # // increment our count. This is why we started at zero. | |
452 | # self.count += 1; | |
453 | # | |
454 | # // check to see if we've finished counting or not. | |
455 | # if self.count < 6 { | |
456 | # Some(self.count) | |
457 | # } else { | |
458 | # None | |
459 | # } | |
460 | # } | |
461 | # } | |
462 | # | |
463 | #[test] | |
464 | fn using_other_iterator_trait_methods() { | |
465 | let sum: u32 = Counter::new().zip(Counter::new().skip(1)) | |
466 | .map(|(a, b)| a * b) | |
467 | .filter(|x| x % 3 == 0) | |
468 | .sum(); | |
469 | assert_eq!(18, sum); | |
470 | } | |
471 | ``` | |
472 | ||
473 | <span class="caption">Listing 13-23: Using a variety of `Iterator` trait | |
474 | methods on our `Counter` iterator</span> | |
475 | ||
476 | Note that `zip` produces only four pairs; the theoretical fifth pair `(5, | |
477 | None)` is never produced because `zip` returns `None` when either of its input | |
478 | iterators return `None`. | |
479 | ||
480 | All of these method calls are possible because we specified how the `next` | |
481 | method works, and the standard library provides default implementations for | |
482 | other methods that call `next`. |