🎓 A1Learner
🔍

Rust Iterators

An iterator is a value that hands out items one at a time. It hands out an item only when asked.

next() hands over one item at a time

An iterator is any type with a next() method. It gives Some(item), or, at the end, None:

let mut it = vec![10, 20].into_iter();
println!("{:?} {:?} {:?}", it.next(), it.next(), it.next());

iter() borrows the items

Use vec.iter() to borrow items as &T — the list stays yours:

let names = vec!["alan", "ada"];
for name in names.iter() { println!("{}", name); }
println!("still owned: {:?}", names);

into_iter() takes the items

Use vec.into_iter() to take items as T. The list is used up — names cannot be used after this loop:

let names = vec!["ada", "grace"];
for name in names.into_iter() { println!("{}", name); }

iter_mut() changes items in place

Use vec.iter_mut() to borrow items as &mut T, so you can change them where they sit:

let mut scores = vec![1, 2, 3];
for s in scores.iter_mut() { *s *= 10; }
println!("{:?}", scores);

Iterators are lazy

Building a chain of steps does no work by itself. Work happens only when something pulls — here, one call to next(). Watch when the doubling line prints:

let mut steps = vec![1, 2, 3].into_iter().map(|n| { println!("doubling {}", n); n * 2 });
println!("built, nothing ran yet");
println!("first = {:?}", steps.next());

Ready-made tools ride along

Any iterator gains many ready-made tools: enumerate, zip, chain, skip, take, and more. Two of them pick a slice out of the middle here:

let picked: Vec<i32> = vec![1, 2, 3, 4, 5].into_iter().skip(1).take(2).collect();
println!("{:?}", picked);

Example

fn main() {
    let names = vec!["ada", "grace", "edsger"];
    for (i, name) in names.iter().enumerate() {
        println!("{}: {}", i + 1, name);
    }
    println!("still owned: {:?}", names);
}

Key points

  • The trait behind into_iter has a name: IntoIterator.
  • You can implement it for your own types.

In this example, names is a Vec of three names. .iter() borrows them one by one. .enumerate() pairs each borrowed name with its position, starting at 0. The loop adds 1 to the position for display and prints 1: ada, 2: grace, 3: edsger. Because .iter() only borrowed the names, names is still fully owned afterwards — the last println! prints the whole list to prove it. The Rust IntoIterator lesson shows how to implement IntoIterator for your own types.

Note

A for loop is just friendly spelling over an iterator — for x in list uses into_iter() under the hood.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

When does the code inside an iterator chain actually run?