Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

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);
}
Output
4 + 16 + 36 = 56
  • iter() starts the belt, handing out a reference to each number.
  • filter keeps only the items for which the closure says true.
  • map changes each item into something new.
  • sum adds 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);
}
Output
["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);
}
Output
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);
}
Output
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

  1. Use filter and count to count the words longer than 3 letters in a sentence.
  2. Use map and collect to turn a list of prices into prices with 18% tax added.
  3. Use zip to print a table of three products and their quantities.