50 Ways to Handle Your Iterator in Rust
Tags: rust, programming
Published: 2022-03-02 16:07:23

<sub>cover image by [Karen Rustad Tölva](https://twitter.com/whoisaldeka/status/1354889640067559425?s=20&t=ks-FVTEMOB9meWFPIFNMzA).</sub>

>   The answer is easy if you
>   Take it logically
>   I'd like to help you in your struggle
>   To be free
>   There must be fifty ways
>   To leave your lover

That was [Paul Simon](https://en.wikipedia.org/wiki/50_Ways_to_Leave_Your_Lover) on lovers. This is me on Rust iterators (yeah, living the life). Maybe not 50, but there are a lot of ways to handle your iterator in Rust. And they are equally _easy if you take them logically_.

## Iterator, IntoInterator, and FromIterator traits

There are three main traits that we should look into to grasp iterators in Rust.

The [Iterator](https://doc.rust-lang.org/std/iter/trait.Iterator.html) trait requires the implementation of a single method called `next()`, which, when called, returns an `Option<Item>`.

```rust  
  pub trait Iterator {
      type Item;

      fn next(&mut self) -> Option<Self::Item>;
  }
```

Calling `next()` will return `Some(Item)` as long as there are elements, and once they’ve all been exhausted, it will return `None` to indicate that iteration is finished. 

In the code below, there is an implementation of the `Iterator` trait for a certain `Counter` type (the example is from the [Introduction to Programming Using Rust](http://gradebot.org/doc/ipur/preface.html) book).

```rust
  struct Counter {
      max: i32,
      // `count` tracks the state of this iterator
      count: i32,
  }

  impl Counter {
      fn new(max: i32) -> Counter {
          Counter { count: -1, max: max }
      }
  }

  impl Iterator for Counter {
      type Item = i32;

      fn next(&mut self) -> Option<Self::Item> {
          self.count += 1;
          if self.count < self.max {
              Some(self.count)
          } else {
              None
          }
      }
  }

  for i in Counter::new(10) {
      println!("{}", i);
  }
```

The [IntoIterator](https://doc.rust-lang.org/std/iter/trait.IntoIterator.html) trait requires the implementation of an `into_iter()` method. This method defines how any data type can be converted into an interator.

```rust
  pub trait IntoIterator {
      type Item;
      type IntoIter: Iterator;
      fn into_iter(self) -> Self::IntoIter;
  }
```

Finally, the [FromIterator](https://doc.rust-lang.org/std/iter/trait.FromIterator.html) trait, obviously, takes an iterator and returns a collection.

```rust
  pub trait FromIterator<A> {
      fn from_iter<T>(iter: T) -> Self
      where
          T: IntoIterator<Item = A>;
  }
```

The synergy between these traits allows an important flexibility when getting a custom type to be iterable. A developer can, of course, implement the `Iterator` trait for the type and be done with it. But they can also cheat a bit, in a sense, by implementing the `IntoIterator` and `FromIterator` traits to produce an existing iterable type, such as a `Vec` and offload the iteration responsibility to this intermediate data type.

Let's look into how the above example for the `Counter` type could have been implemented this way by using [Range](https://doc.rust-lang.org/std/ops/struct.Range.html) from the standard library (again, the example is from the [Introduction to Programming Using Rust](http://gradebot.org/doc/ipur/preface.html) book).

```rust
  struct Counter {
      max: i32,
      // No need to track the state, 
      // because this isn't an iterator.
  }

  impl Counter {
      fn new(max: i32) -> Counter {
          Counter { max: max }
      }
  }

  impl IntoIterator for Counter {
      type Item = i32;
      type IntoIter = std::ops::Range<Self::Item>;

      fn into_iter(self) -> Self::IntoIter {
          std::ops::Range{ start: 0, end: self.max }
      }
  }

  for i in Counter::new(10) {
      println!("{}", i);
  }
```

## into_iter, iter and iter_mut methods

We have already seen that the `IntoIterator` trait has only one required method, `into_iter()` which converts the thing implementing `IntoIterator` into, well, an iterator.

Additionally, collection types generally offer methods that provide iterators over references, called `iter()` and `iter_mut()`. All these do is borrow either an immutable or mutable reference to the collection and turn it into an iterator.

In other words, if you're confused when to use which, remember that the king is `into_iter()`, and `iter()` and `iter_mut()` are just wrapper methods to borrow and into_iter in one go.

```rust
  // assuming lovers is Vec<Lover>

  // Iter<Vec<Lover>>
  // because: impl<'a, T> IntoIterator for Vec<T>
  lovers.into_iter()  

  // Iter<&Vec<Lover>>
  // because: impl<'a, T> IntoIterator for &'a Vec<T>
  lovers.iter()       
  
  // Iter<&mut Vec<Lover>>
  // because: impl<'a, T> IntoIterator for &'a mut Vec<T>
  lovers.iter_mut()   
```

## for loops

Rust's `for` loop is just syntactic sugar for iterators.

So this:

```rust
  // assuming lovers is Vec<Lover>
  for lover in lovers {
      println!("{}", lover);
  }
```

is actually this:

```rust
  // assuming lovers is Vec<Lover>
  {
      let struggle = match IntoIterator::into_iter(lovers) {
          mut iter => loop {
              let next;

              match iter.next() {
                  // that's what she said
                  Some(lover) => next = lover,
                  
                  // said no one ever 
                  None => break,                 
              };

              let lover = next;

              let () = { println!("{}", lover); };
          },
      };

      struggle
  }
```

As we have seen, `into_iter()` takes self, using a for loop to iterate over a collection consumes that collection. Yet, it's possible to loop with `for` without consuming. Remember, for a `Vec`, the `IntoIterator` trait is implemented multiple times.

```rust
  impl<T> IntoIterator for Vec<T>
  impl<'a, T> IntoIterator for &'a Vec<T>
  impl<'a, T> IntoIterator for &'a mut Vec<T>
```

Basically, if we loop over a vector directly, we'll consume it and get owned vector items within the loop. But if we loop over an immutable or mutable (& and &mut) reference to a vector, we'll get an immutable or mutable reference to vector items within the loop.

```rust
  // assuming lovers is Vec<Lover>


  // consuming lovers, lover is an owned Lover
  // ^_^ (see what I did there)
  for lover in lovers {}          
                                  
  // lover is &Lover
  // (because you can't own a lover)
  for lover in &lovers {}         
                                  
  // lover is &mut Lover
  // (but people don't change)
  for lover in &mut lovers {}     
                                  
```

## Lazy Iterators

As a final note, in Rust, iterators are lazy, meaning they have no effect until you call methods that consume the iterator. 

Methods for consuming an iterator may be defined on the `Iterator` trait and are called consuming adapters because they fulfill whatever their purpose is by consuming the iterator via calling `next()` on it. Methods such as `reduce()`, `find()`, `sum()`, or `collect()` are consuming adapters.

On the other hand, certain methods called iterator adapters may also be defined on the `Iterator` trait. These take an iterator and return another. These methods are called between the creation of an iterator and its consumption. The primary example is, of course, `map()`. As a result of laziness, `map()` will do nothing unless fed into a consuming adapter.