An iterator is a value that remembers where it is. Calling a short-circuiting method like .any() doesn't peek at the front of the sequence and leave it untouched — it pulls real items through next() until it finds a match, and those items are gone for good. Keep using that same iterator afterward (a second for loop, say) and you silently pick up wherever the first call left off, not from the start.
let orders = vec![12, 45, 7, 99, 3, 61];
let mut iter = orders.iter();
let has_over_90 = iter.any(|&x| x > 90); // scans 12, 45, 7, 99 -- stops at 99
println!("found an order over 90: {}", has_over_90);
println!("remaining orders to process:");
for order in iter { // continues the SAME iterator -- not a fresh pass!
println!(" {}", order);
}
// prints only 3 and 61 -- the four orders already scanned by .any() are goneThe Iterator trait requires just one method, next(), which returns Some(item) or None when exhausted. Iterators are lazy: calling .map() or .filter() builds up a chain of transformations but produces no output on its own — nothing runs until a consuming method like .collect(), .sum(), or a for loop actually pulls values through the chain. Because of how the compiler optimizes these chains, iterator code typically compiles to loops just as fast as hand-written ones.
Creating and consuming an iterator
.iter() produces an iterator of references over a collection; a for loop is a consuming method that pulls every item through automatically.
let v = vec![1, 2, 3];
let mut iter = v.iter();
println!("{:?}", iter.next()); // Some(1)
println!("{:?}", iter.next()); // Some(2)
println!("{:?}", iter.next()); // Some(3)
println!("{:?}", iter.next()); // None — exhausted
for x in v.iter() { // idiomatic: for loop consumes the iterator
println!("{}", x);
}That's the same state the bug above depends on: iter.any(...) advances the one iter object past every item it checks, including the match itself, so a for loop over that same iter afterward only sees what's left. Asking the collection for a brand-new iterator -- orders.iter() again, rather than reusing iter -- starts over from the front:
let orders = vec![12, 45, 7, 99, 3, 61];
let has_over_90 = orders.iter().any(|&x| x > 90);
println!("found an order over 90: {}", has_over_90);
println!("remaining orders to process:");
for order in orders.iter() { // a fresh iterator, starts from the beginning
println!(" {}", order);
}
// prints all six ordersLaziness: nothing happens until you consume
Building a chain of adapters like map and filter creates a description of the work, but performs none of it — the underlying closures are never even called — until a consuming method drives the chain to completion.
let v = vec![1, 2, 3, 4, 5];
let chain = v.iter().map(|x| {
println!("processing {}", x); // this print never runs yet!
x * 2
});
// nothing has printed at this point — chain hasn't been consumed
let doubled: Vec<i32> = chain.collect(); // NOW it runs, prints 5 times
println!("{:?}", doubled); // [2, 4, 6, 8, 10]Common adapters: map, filter, collect
Adapter methods chain together to express a data transformation declaratively; .collect() is the most common consumer, gathering results into a Vec, String, HashMap, or any type implementing FromIterator.
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0) // keep evens: [2, 4, 6]
.map(|x| x * x) // square them: [4, 16, 36]
.collect();
println!("{:?}", result); // [4, 16, 36]
let total: i32 = numbers.iter().sum(); // consumes directly, no collect needed
println!("{}", total); // 21