Skip to content

Iterators

Allows to iterate over a sequence of elements, independently how they are stored. All iterators implement the iterator trait.

fn main() {

  let v1 = vec![1, 2, 3];

  let v1_iter = v1.iter();

  for value in v1_iter {
    println!("Got: {}" ,value);
  }
}

Consumers vs Adaptors

Two categories of Iterator methods. The adaptors (takes an iterator and returns an iterator) and consumers (takes iterators and returns another type)

// example of consumer method
#[test]
fn iterator_sum() {
  let v1 = vec![1, 2, 3];
  let v1_iter = v1. iter();
  let total: i32 = v1_iter.sum();

  assert_eq!(total, 6);
}

// example of adapter method ``map()``
fn main()
  let v1: Vec<i32> = vec![1, 2, 3];
  let v2: Vec<i32> = v1.iter().map(|x| x + 1).collect();

  assert_eq!(v2, ver![2, 3, 4]);
}

Iterators and Closures

#[derive(PartialEq, Debug)]
struct Shoe {
  size: u32,
  style: String,
}

fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
  shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}

fn main() {}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn filters_by_size() {
    let shoes = vec![
     Shoe{
       size: 10,
       style: String::from("sneaker"),
     },
     Shoe{
       size: 13,
       style: String::from("sandal"),
     },
     Shoe{
       size: 10,
       style: String::from("boot"),
     },
    ];

    let in_my_size = shoes_in_my_size(shoes, 10);

    assert_eq!(
      in_my_size,
      vec![
        Shoe{
          size: 10,
          style: String::from("sneaker"),
        },
        Shoe{
          size: 10,
          style: String::from("boot"),
        },
      ]
    );
  }
}

Iterator Implementation

Example of a Iterator implementation

pub trait Iterator {
  type Item;   // associated type, defined in later

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

  // methods with default implementation not done
}

#[test]
fn iterator_demo() {
  let v1 = vec![1, 2, 3];

  let mut v1_iter = v1.iter();     // returns immutable

  asser_eq!(v1_iter.next(), Some(&1));
  asser_eq!(v1_iter.next(), Some(&2));
  asser_eq!(v1_iter.next(), Some(&3));
  asser_eq!(v1_iter.next(), None;

  let mut v1_iter = v1.iter_mut(); // return mutable

  asser_eq!(v1_iter.next(), Some(&1));
  asser_eq!(v1_iter.next(), Some(&2));
  asser_eq!(v1_iter.next(), Some(&3));
  asser_eq!(v1_iter.next(), None;

  let mut v1_iter = v1.iter_mut(); // return values directly
  asser_eq!(v1_iter.next(), Some(1));
  asser_eq!(v1_iter.next(), Some(2));
  asser_eq!(v1_iter.next(), Some( 3));
  asser_eq!(v1_iter.next(), None;
}

Another example of a Iterator implementation

struct Counter {
  count: u32,       // memory for the iterator, private
}

impl Counter {
  fn new() -> Counter {
    Counter { count: 0 }  // count gets initialised to 0
  }
}

impl Iteraotr for Counter {
  type Item = u32;    // associated type (returns items of type u32)

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

#[test]
fn calling_next_directly() {
  let mut counter = Counter::new();

  assert_eq!(counter.next(), Some(1));
  assert_eq!(counter.next(), Some(2));
  assert_eq!(counter.next(), Some(3));
  assert_eq!(counter.next(), Some(4));
  assert_eq!(counter.next(), Some(5));
  assert_eq!(counter.next(), None;
}

#[test]
fn using_other_iterator_trait_methods() {
  let sum: u32 = Counter::new();
     .zip(Counter::new().skip(1))  // zip takes 2 iterators and bundlers them together
                                   // skip is an adaptor method returning a iterator skiped by 1 element
     .map(|(a,b)| a * b)           // map takes a closure for each element returns a iterator
     .filter(|x| x % 3 == 0)       // filters
     .sum();                       // sum of all iterator values
  assert_eq!(18,sum);
}

fn main() {}