August Feng

Learning Rust iterators

Iterators

By definition, Iterators are structs that implement the IntoIterator trait. There is also the Iterator trait, will inadvertently implements the IntoIterator trait:

  impl<I: Iterator> const IntoIterator for I {
      type Item = I::Item;
      type IntoIter = I;

      #[inline]
      fn into_iter(self) -> I {
          self
      }
  }

The implementation of the IntoIterator trait enables the usage of the for loop construct on the object.

  struct Foobar {
      s: String,
  }

  struct Foobaz {
      s: String,
  }

  impl Iterator for Foobar {
      type Item = char;
      fn next(&mut self) -> Option<Self::Item> {
          if self.s.len() > 0 {
              Some(self.s.remove(0))
          } else {
              None
          }
      }
  }

  // Manual implementation of *IntoIterator* by piggy-backing on ~Foobar~ who
  // indirectly implements *IntoIterator*.
  impl IntoIterator for Foobaz {
      type Item = char;
      type IntoIter = Foobar;
      fn into_iter(self) -> Self::IntoIter {
          Foobar { s: self.s }
      }
  }

  fn main() {
      let a = Foobar {
          s: String::from("hello"),
      };
      for it in a.into_iter() {
          println!("{}", it)
      }

      let b = Foobaz {
          s: String::from("world"),
      };
      for it in b.into_iter() {
          println!("{}", it)
      }

      // same

      let c = vec!['h', 'e', 'l', 'l', 'o'];
      for it in c.iter() {
          println!("{}", it)
      }

      let d = vec!['h', 'e', 'l', 'l', 'o'];
      for it in &d {
          println!("{}", it)
      }

      // same

      let mut e = vec!['h', 'e', 'l', 'l', 'o'];
      for it in e.iter_mut() {
          println!("{}", it)
      }

      let mut f = vec!['h', 'e', 'l', 'l', 'o'];
      for it in &mut f {
          println!("{}", it)
      }
  }

There is also the conventional iter() and iter_mut() method which don't consume the vector upon iteration. These methods are not standardized under a trait and will generally be also be available as into_iter() when the values are bound to a (mutable?) reference.

Case Study

Vec

The Vec struct only implements the IntoIterator trait. It uses a struct (IntoIter) in the backend for plumbing.

There are three different implementations.

value

  impl<T, A: Allocator> IntoIterator for Vec<T, A> {
      type Item = T;
      type IntoIter = IntoIter<T, A>;

      /// Creates a consuming iterator, that is, one that moves each value out of
      /// the vector (from start to end). The vector cannot be used after calling
      /// this.
      ///
      /// # Examples
      ///
      /// ```
      /// let v = vec!["a".to_string(), "b".to_string()];
      /// let mut v_iter = v.into_iter();
      ///
      /// let first_element: Option<String> = v_iter.next();
      ///
      /// assert_eq!(first_element, Some("a".to_string()));
      /// assert_eq!(v_iter.next(), Some("b".to_string()));
      /// assert_eq!(v_iter.next(), None);
      /// ```
      #[inline]
      fn into_iter(self) -> Self::IntoIter {
          unsafe {
              let mut me = ManuallyDrop::new(self);
              let alloc = ManuallyDrop::new(ptr::read(me.allocator()));
              let begin = me.as_mut_ptr();
              let end = if T::IS_ZST {
                  begin.wrapping_byte_add(me.len())
              } else {
                  begin.add(me.len()) as *const T
              };
              let cap = me.buf.capacity();
              IntoIter {
                  buf: NonNull::new_unchecked(begin),
                  phantom: PhantomData,
                  cap,
                  alloc,
                  ptr: begin,
                  end,
              }
          }
      }
  }

reference

  impl<'a, T, A: Allocator> IntoIterator for &'a Vec<T, A> {
      type Item = &'a T;
      type IntoIter = slice::Iter<'a, T>;

      fn into_iter(self) -> Self::IntoIter {
          self.iter()
      }
  }

mutable reference

  impl<'a, T, A: Allocator> IntoIterator for &'a mut Vec<T, A> {
      type Item = &'a mut T;
      type IntoIter = slice::IterMut<'a, T>;

      fn into_iter(self) -> Self::IntoIter {
          self.iter_mut()
      }
  }