[Avg. reading time: 14 minutes]

Iterator in Rust

What is Iteration?

Iteration means traversing a collection of values one element at a time.

Examples of collections include:

  • Arrays
  • Vectors
  • HashMaps
  • Slices

In Rust, iteration can be done using:

  • Traditional loops
  • Iterators

Both approaches allow you to process elements in a collection, but they follow different programming styles.


Loops vs Iterators

Loops – Imperative Style

Loops follow an imperative programming style.

In this approach you explicitly control how the loop operates.

You must manage:

  • Index position
  • Loop bounds
  • Increment logic

Example

fn main() {
    let ages = [27, 35, 40, 10, 19];
    let mut index = 0;

    while index < ages.len() {
        let age = ages[index];
        println!("Age = {:?}", age);
        index += 1;
    }
}

Here you manually:

  • Track the position
  • Check the array length
  • Increment the counter

The following code will panic because the loop goes out of bounds.

fn main() {
    let ages = [27, 35, 40, 10, 19];
    let mut index = 0;

    while index <= ages.len() {
        let age = ages[index];
        println!("Age = {:?}", age);
        index += 1;
    }
}

Iterators – Declarative Style

Iterators follow a declarative programming style. Instead of describing how to loop, you describe what should happen to each element. Rust handles the iteration logic internally.

Benefits

  • Cleaner code
  • Safer iteration
  • Less chance of index errors
  • Encourages functional programming patterns
fn main() {
    let ages = [27, 35, 40, 10, 19];

    let ages_iterator = ages.iter();

    for age in ages_iterator {
        println!("Age = {:?}", age);
    }
}

Developer doesnt have to manage the

  • Index
  • Length
  • Counter increment

Rust abstracts that away.

Iterator Trait

In Rust, iterators are based on a trait called Iterator.

Every iterator must implement the next() method.

fn main() {
    let ages = [27, 35, 40, 10, 19];
    let mut ages_iterator = ages.iter();

    println!("{:?}", ages_iterator.next());
    println!("{:?}", ages_iterator.next());
    println!("{:?}", ages_iterator.next());
    println!("{:?}", ages_iterator.next());
    println!("{:?}", ages_iterator.next());
    println!("{:?}", ages_iterator.next());
}

Each call to next() moves the iterator forward.

Iterating using while let

fn main() {
    let ages = [27, 35, 40, 10, 19];
    let mut ages_iterator = ages.iter();

    while let Some(age) = ages_iterator.next() {
        println!("{:?}", age);
    }
}

Creating Iterators

Rust collections provide several ways to create iterators.

iter()

Returns an iterator over references.

fn main() {
    let ages = vec![27, 35, 40];

    for age in ages.iter() {
        println!("{}", age);
    }
}

iter_mut()

Returns mutable references allowing values to be modified.

fn main() {
    let mut ages = vec![27, 35, 40];

    for age in ages.iter_mut() {
        *age += 1;
    }

    println!("{:?}", ages);
}

into_iter()

Consumes the collection and moves the values.

fn main() {
    let ages = vec![27, 35, 40];

    for age in ages.into_iter() {
        println!("{}", age);
    }

    // ages cannot be used here because ownership moved
}

Iterator Adapters

Iterator adapters transform iterators into new iterators.

Common adapters include:

  • map
  • filter
  • enumerate
  • take
  • skip
fn main() {
    let ages = [27, 35, 40, 10, 19];

    let adults: Vec<i32> = ages
        .iter()
        .copied()
        .filter(|age| *age >= 18)
        .map(|age| age * 2)
        .collect();

    println!("{:?}", adults);
}
Collection
   ↓
Iterator
   ↓
Filter
   ↓
Map
   ↓
Collect

Iterator Consumers

Consumers produce a final result from an iterator.

Common consumers include:

  • collect()
  • sum()
  • count()
  • fold()
  • for_each()
fn main() {
    let numbers = [1, 2, 3, 4, 5];

    let total: i32 = numbers.iter().sum();
    let total_length: usize = numbers.iter().count();

    println!("{},{}", total, total_length);
}

Lazy Evaluation

Rust iterators use lazy evaluation.

Operations do not run immediately.

fn main() {
    let ages = [27, 35, 40];

    let result = ages
        .iter()
        .map(|age| {
            println!("Processing {}", age);
            age * 2
        });

    println!("Iterator created but not consumed");
}

Using collect()

Execution begins once a consumer is used.

fn main() {
    let ages = [27, 35, 40];

    let result: Vec<_> = ages
        .iter()
        .map(|age| {
            println!("Processing {}", age);
            age * 2
        })
        .collect();

    println!("{:?}", result);
}
Collection
   ↓
Iterator
   ↓
map()
   ↓
collect()
   ↓
Execution happens

enumerate()

enumerate() adds an index to each element of an iterator.

fn main() {
    let ages = [27, 35, 40, 10];

    for (index, age) in ages.iter().enumerate() {
        println!("Index: {}, Age: {}", index, age);
    }
}
ages
 ↓
iter()
 ↓
Iterator
 ↓
enumerate()
 ↓
(index, value)

take()

take(n) returns only the first n elements from an iterator.

fn main() {
    let numbers = [10, 20, 30, 40, 50];

    for value in numbers.iter().take(3) {
        println!("{}", value);
    }
}
numbers.iter()
   ↓
take(3)
   ↓
iterator stops after 3 elements

skip()

skip(n) ignores the first n elements and starts iteration after that.

fn main() {
    let numbers = [10, 20, 30, 40, 50];

    for value in numbers.iter().skip(2) {
        println!("{}", value);
    }
}

Use Cases

Iterators are widely used in systems and data processing.

Common use cases include:

  • Stream log processing
  • Network packet processing
  • ETL style pipelines
  • CLI tools
  • Data transformation pipelines
numbers.iter()
   ↓
skip(2)
   ↓
iteration begins at element 3

#iterator #loops #declarativeVer 2.0.19

Last change: 2026-03-04