Rust Iterators
An iterator produces values one at a time. Every for loop you have written used one behind the scenes. But iterators can do much more than feed a loop: they have methods that transform, filter and combine values, and you can chain these methods into small pipelines that read almost like a sentence.
Picture a factory conveyor belt. Items come along one by one; one machine paints them, the next throws away the broken ones, and at the end someone counts what is left. That is an iterator chain.
map, filter and sum
fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let total_of_even_squares: i32 = numbers
.iter()
.filter(|&&n| n % 2 == 0)
.map(|n| n * n)
.sum();
println!("4 + 16 + 36 = {}", total_of_even_squares);
}4 + 16 + 36 = 56
iter()starts the belt, handing out a reference to each number.filterkeeps only the items for which the closure saystrue.mapchanges each item into something new.sumadds up everything that reaches the end.
Iterators are lazy: nothing happens until a final step such as sum, count or collect asks for the values. And thanks to Rust's compiler, a chain like this runs as fast as a hand-written loop.
collect builds a collection
collect gathers the results into a new collection. Tell Rust which kind you want with a type on the variable:
fn main() {
let names = vec!["asha", "ravi", "meera"];
let capitals: Vec<String> = names.iter().map(|n| n.to_uppercase()).collect();
println!("{:?}", capitals);
let lengths: Vec<usize> = names.iter().map(|n| n.len()).collect();
println!("{:?}", lengths);
}["ASHA", "RAVI", "MEERA"] [4, 4, 5]
More useful steps
fn main() {
let scores = [72, 95, 58, 88, 64, 91];
let passed = scores.iter().filter(|&&s| s >= 60).count();
println!("Passed: {}", passed);
let top_three: Vec<_> = scores.iter().rev().take(3).collect();
println!("Last three, newest first: {:?}", top_three);
println!("Any above 90? {}", scores.iter().any(|&s| s > 90));
println!("All above 50? {}", scores.iter().all(|&s| s > 50));
let best = scores.iter().max().unwrap();
let position = scores.iter().position(|s| s == best).unwrap();
println!("Best score {} is at position {}", best, position);
}Passed: 5 Last three, newest first: [91, 64, 88] Any above 90? true All above 50? true Best score 95 is at position 1
zip: walking two lists together
zip pairs up the items of two iterators, the first with the first, the second with the second:
fn main() {
let students = ["Asha", "Ravi", "Leela"];
let marks = [91, 78, 85];
for (name, mark) in students.iter().zip(marks.iter()) {
println!("{} got {}", name, mark);
}
let total: i32 = marks.iter().sum();
println!("Class average: {:.1}", total as f64 / marks.len() as f64);
}Asha got 91 Ravi got 78 Leela got 85 Class average: 84.7
Loops or iterators?
Both are good Rust. Loops are easier while you are learning, and some jobs read more clearly as a loop. Iterator chains shine when you are transforming data step by step. Try writing small problems both ways; after a while you will feel which one suits each job.
Practice exercises
- Use
filterandcountto count the words longer than 3 letters in a sentence. - Use
mapandcollectto turn a list of prices into prices with 18% tax added. - Use
zipto print a table of three products and their quantities.