🎓 A1Learner
🔍
0 of 149 lessons completed

Rust Consuming Iterators

A consuming iterator method pulls items through a chain and returns a final answer. sum, count, and collect are consumers.

sum and count finish a chain

sum adds the items. count counts them. Both pull until the iterator is empty. In short, a consumer pulls items through the chain and returns the final answer:

let total: i32 = (1..=4).sum();
println!("sum = {total}, count = {}", (1..=4).count());

Multiply everything with product

sum adds. product multiplies. It is the natural way to write a factorial:

let fact: i32 = (1..=5).product();
println!("5! = {fact}");

collect builds the collection you ask for

collect gathers items into the type you name — here, single characters become one String:

let word: String = ['r', 'u', 's', 't'].into_iter().collect();
println!("{word}");

find and any answer questions

find returns the first item that passes, as an Option. any answers true or false:

let first_even = [1, 3, 4].iter().find(|&&n| n % 2 == 0);
let any_big = [1, 3, 4].iter().any(|n| *n > 10);
println!("{first_even:?} {any_big}");

Jump ahead with nth, finish with last

nth(2) skips two items and returns the third — the skipped ones are consumed. last() pulls everything and keeps only the final item:

let mut it = vec!["a", "b", "c", "d"].into_iter();
println!("nth(2) = {:?}", it.nth(2));
println!("next = {:?}", it.next());
let last: Option<i32> = (5..9).last();
println!("last = {last:?}");

min and max without a key

When the items compare directly, plain min and max need no key closure. Both still return Option. An empty iterator has no winner:

let low: Option<i32> = vec![9, 4, 7].into_iter().min();
let high: Option<i32> = vec![9, 4, 7].into_iter().max();
println!("min = {low:?} max = {high:?}");

Example

fn main() {
    let total: i32 = (1..=100).filter(|n| n % 3 == 0).sum();
    println!("sum of multiples of 3 up to 100 = {}", total);
    let (evens, odds): (Vec<i32>, Vec<i32>) = (1..=6).partition(|n| n % 2 == 0);
    println!("evens {:?} odds {:?}", evens, odds);
}

Key points

  • Use fold(start, f) for a custom accumulation.
  • sum is a fold in disguise.
  • Use for_each(f), or a plain for loop, when you only need side effects like printing
  • find hands the closure a reference to the item, &Item.
  • Over .iter() the item already is a reference.
  • So the pattern doubles up as |&&x|.
  • find gives back the found reference.
  • position hands the item itself and gives back its index number.
  • Use min_by_key(f) and max_by_key(f) to find the item with the smallest or largest measured value.
  • Both return an Option.
  • An empty iterator has no winner.
  • A consumer ends the pipeline.

In this example, the range 1..=100 is filtered to multiples of 3, and sum() adds them: 1683. Below, partition walks 1..=6 once and sorts each number into one of two Vecs by a simple test, "is it even?". The result is evens [2, 4, 6] and odds [1, 3, 5]. Notice that both answers came from one pass of pulling — consumers never need the items twice.

Note

collect can even gather Results: collecting into Result<Vec<T>, E> stops at the first Err.

One failure fails the whole collection — a neat way to validate a list of inputs.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What happens when you collect an iterator of Results into a Result<Vec<T>, E>?