DEV Community

Cover image for [Rust Guide] 13.8. Creating Custom Iterators
SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 13.8. Creating Custom Iterators

13.8.0 Before We Begin

During its design, Rust drew inspiration from many languages, and functional programming had a particularly strong influence on Rust. Functional programming often includes passing functions as values to parameters, returning them from other functions, assigning them to variables for later execution, and so on.

In this chapter, we will discuss some Rust features that are similar to what many languages call functional features:

  • Closures
  • Iterators (this article)
  • Improving the I/O Project with Closures and Iterators
  • Performance of Closures and Iterators

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

13.8.1 Creating a Custom Iterator With the Iterator Trait

The main step is just one: provide an implementation of next.

Take an example:

Build an iterator that traverses from 1 to 5

struct Counter {  
    count: u32,  
}  

impl Counter {  
    fn new() -> Counter {  
        Counter { count: 0 }  
    }  
}  

impl Iterator for Counter {  
    type Item = u32;  
    fn next(&mut self) -> Option<u32> {  
        if self.count < 5 {  
            self.count += 1;  
            Some(self.count)  
        } else {  
            None  
        }  
    }  
}
Enter fullscreen mode Exit fullscreen mode
  • First create a struct called Counter. It has a count field used to store the value needed during iteration, that is, the iterator’s state. The count field is private rather than pub so that the Counter struct manages its own value independently.

  • Then write an associated function new on the struct to create a new instance and make sure the new instance starts from 0.

  • Next, implement the Iterator trait for Counter. The Iterator trait has an associated type type Item and a next method. First set the associated type to u32, which means writing type Item = u32;. This syntax will be covered in detail in Chapter 19; for now, just know that this iterator returns u32.

  • The return type of next is Option<Self::Item>. Since the associated type above is u32, you can think of it as Option<u32>. When the count field is less than 5, it keeps increasing by 1; when it is 5 or greater, it returns None. That guarantees iteration from 1 to 5.

Now let’s implement something more complex:

Use a Counter struct in two forms: one from 1 to 5 and another from 2 to 5. Multiply each pair of elements from the two iterators, keep only the elements in the new iterator that are divisible by 3, and then return the sum of those elements

fn using_other_iterator_traits_methods() {  
    let sum: u32 = Counter::new()  
        .zip(Counter::new().skip(1))  
        .map(|(a, b)| a * b)  
        .filter(|x| x % 3 == 0)  
        .sum();  
}
Enter fullscreen mode Exit fullscreen mode
  • I wrote it on separate lines because the chained call would be too long on one line. If the chain is not long, there is no need to split it across lines.
  • The zip method pairs each element from two iterators to form a new iterator. The elements of that new iterator are tuples, and each tuple has two values, one from each iterator.
  • Counter::new() creates a Counter that goes from 1 to 5, and Counter::new().skip(1) creates a Counter that skips the first value, so it goes from 2 to 5. When the two are zipped together, the result looks like this:
Counter::new() Counter::new().skip(1)
Tuple 0 1 2
Tuple 1 2 3
Tuple 2 3 4
Tuple 3 4 5

PS: Counter::new() does not iterate to 5 because Counter::new().skip(1) is None at that point, so no more values are produced.

  • map takes a closure that is applied to each element of the iterator. It transforms each element of the current iterator into another element, and those new elements form a new iterator. In this example, it multiplies the two values in each tuple stored in the iterator to produce a new iterator.
  • filter keeps the values divisible by 3 and forms a new iterator through the closure.
  • sum consumes all elements of the iterator and adds them together.

The final result should be 18.

Top comments (0)