Iterators and Ownership

The ownership model of Rust affects many APIs. An example of this is the Iterator and IntoIterator traits.

Iterator

Traits are like interfaces: they describe behavior (methods) for a type. The Iterator trait simply says that you can call next until you get None back:

  1. #![allow(unused)]
  2. fn main() {
  3. pub trait Iterator {
  4. type Item;
  5. fn next(&mut self) -> Option<Self::Item>;
  6. }
  7. }

You use this trait like this:

  1. fn main() {
  2. let v: Vec<i8> = vec![10, 20, 30];
  3. let mut iter = v.iter();
  4. println!("v[0]: {:?}", iter.next());
  5. println!("v[1]: {:?}", iter.next());
  6. println!("v[2]: {:?}", iter.next());
  7. println!("No more items: {:?}", iter.next());
  8. }

What is the type returned by the iterator? Test your answer here:

  1. fn main() {
  2. let v: Vec<i8> = vec![10, 20, 30];
  3. let mut iter = v.iter();
  4. let v0: Option<..> = iter.next();
  5. println!("v0: {v0:?}");
  6. }

Why is this type used?

IntoIterator

The Iterator trait tells you how to iterate once you have created an iterator. The related trait IntoIterator tells you how to create the iterator:

  1. #![allow(unused)]
  2. fn main() {
  3. pub trait IntoIterator {
  4. type Item;
  5. type IntoIter: Iterator<Item = Self::Item>;
  6. fn into_iter(self) -> Self::IntoIter;
  7. }
  8. }

The syntax here means that every implementation of IntoIterator must declare two types:

  • Item: the type we iterate over, such as i8,
  • IntoIter: the Iterator type returned by the into_iter method.

Note that IntoIter and Item are linked: the iterator must have the same Item type, which means that it returns Option<Item>

Like before, what is the type returned by the iterator?

  1. fn main() {
  2. let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
  3. let mut iter = v.into_iter();
  4. let v0: Option<..> = iter.next();
  5. println!("v0: {v0:?}");
  6. }

for Loops

Now that we know both Iterator and IntoIterator, we can build for loops. They call into_iter() on an expression and iterates over the resulting iterator:

  1. fn main() {
  2. let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
  3. for word in &v {
  4. println!("word: {word}");
  5. }
  6. for word in v {
  7. println!("word: {word}");
  8. }
  9. }

What is the type of word in each loop?

Experiment with the code above and then consult the documentation for impl IntoIterator for &Vec and impl IntoIterator for Vec to check your answers.