| |

Rust 52 🦀 Iterator Trait and Consumers vs Adapters

⏱️ Estimated read: 17 min | |

An iterator is a value that produces a sequence of items, one at a time, on demand. Every collection in Rust can produce an iterator, and every iterator can be composed with adapters and driven to completion by consumers. The Iterator trait defines one required method, next, and dozens of provided methods that build on it. The distinction between adapters and consumers is the organizing principle of the entire API: adapters are lazy and return new iterators, consumers are eager and return a final value. Understanding where that line falls is what lets a developer write iterator chains that are readable, efficient, and correct.

Key point: Adapters are lazy. They do nothing until a consumer drives the chain. A chain of adapters without a consumer compiles but does nothing, and the compiler warns about it. Every iterator chain ends in exactly one consumer.


Why the adapter-consumer split exists

The laziness problem. An eager API would process every transformation on every element immediately. Mapping a million-element list, filtering it, and taking the first three would do a million maps and a million filters to produce three results. A lazy API defers the work: adapters describe what should happen, and the consumer pulls items through the chain one at a time, doing only the work required.

The composition problem. Adapters return iterators, so they can be chained. Each adapter wraps the previous iterator, and each next call propagates through the chain. The result is a pipeline that is assembled once and pulled on demand.

The zero-cost problem. Iterator chains compile to code equivalent to hand-written loops. The compiler inlines the adapter calls and the consumer’s loop, eliminating the abstraction overhead. There is no allocation per element and no dynamic dispatch unless a trait object is used.

The infinite iterator problem. Because adapters are lazy, an iterator can be infinite. (1..).map(|x| x * 2) is an infinite iterator of even numbers. The chain does no work until a consumer pulls from it, and a consumer like take(5) limits the pull to five items.

The single-pass problem. An iterator is consumed by its consumer. Once a consumer runs, the iterator is exhausted. This is a consequence of the next method taking &mut self, which makes the iterator stateful.

The API surface problem. The Iterator trait has over seventy provided methods. Without a clear split between adapters and consumers, the API would be overwhelming. The split gives the methods a conceptual structure: adapters build, consumers finish.


a. The Iterator trait and the next method

The Iterator trait has one required method, next, and everything else is provided in terms of it.

The next method

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // ... provided methods ...
}

The associated type Item is the type of each element. The next method returns Some(item) for the next element or None when the iterator is exhausted. The &mut self receiver means the iterator is mutated as it is advanced.

fn main() {
    let mut iter = vec![1, 2, 3].into_iter();
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next(), Some(2));
    assert_eq!(iter.next(), Some(3));
    assert_eq!(iter.next(), None);
}

After None, the iterator is exhausted. Calling next again returns None or is unspecified, depending on the iterator.

The IntoIterator trait

A type is iterable if it implements IntoIterator. The for loop uses this trait.

pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

Collections implement IntoIterator for three forms: T (owned), &T (shared reference), and &mut T (mutable reference). The for loop calls the appropriate form based on the binding.

fn main() {
    let v = vec![1, 2, 3];

    for x in &v { /* x: &i32 */ }
    for x in &mut v { /* x: &mut i32 */ }
    for x in v { /* x: i32 */ }
}

The into_iter method is called automatically by the for loop and by adapter methods that accept IntoIterator.

The three iteration forms

FormItem typeOwnership
into_iter()TConsumes the collection
iter()&TBorrows shared
iter_mut()&mut TBorrows mutable

The choice determines what the closure can do with each element. A chain over iter() produces references, and the closures receive &T. A chain over into_iter() produces owned values, and the closures can consume them.

The fused iterator

Some iterators implement FusedIterator, which guarantees that next keeps returning None after the first None. This allows the compiler to optimize chains that check the iterator’s state.

use std::iter::FusedIterator;

// Most standard library iterators are fused.

A fused iterator is safe to call repeatedly after exhaustion.


b. Adapters

An adapter takes an iterator and returns a new iterator. Adapters are lazy: calling them does no work. The work happens when a consumer pulls from the resulting iterator.

map

map applies a function to each item and yields the results.

let doubled: Vec<i32> = vec![1, 2, 3].iter().map(|x| x * 2).collect();
assert_eq!(doubled, vec![2, 4, 6]);

The closure receives &i32 because the chain started with iter(). The output is i32.

filter

filter keeps items for which the predicate returns true.

let evens: Vec<i32> = vec![1, 2, 3, 4, 5].into_iter().filter(|x| x % 2 == 0).collect();
assert_eq!(evens, vec![2, 4]);

The predicate receives &i32 because filter passes each item by reference.

filter_map

filter_map applies a function that returns an Option and keeps the Some values.

let parsed: Vec<i32> = vec!["1", "two", "3"].into_iter()
    .filter_map(|s| s.parse::<i32>().ok())
    .collect();
assert_eq!(parsed, vec![1, 3]);

The chain parses each string and drops the ones that fail.

flat_map

flat_map applies a function that returns an iterator and flattens the results.

let words: Vec<&str> = vec!["hello world", "foo bar"].into_iter()
    .flat_map(|s| s.split_whitespace())
    .collect();
assert_eq!(words, vec!["hello", "world", "foo", "bar"]);

Each input string is split into words, and the words are concatenated.

chain

chain concatenates two iterators.

let chained: Vec<i32> = vec![1, 2].into_iter()
    .chain(vec![3, 4].into_iter())
    .collect();
assert_eq!(chained, vec![1, 2, 3, 4]);

The second iterator is not pulled until the first is exhausted.

zip

zip pairs items from two iterators.

let pairs: Vec<(i32, &str)> = vec![1, 2, 3].into_iter()
    .zip(vec!["a", "b", "c"].into_iter())
    .collect();
assert_eq!(pairs, vec![(1, "a"), (2, "b"), (3, "c")]);

zip stops when either iterator is exhausted.

take and skip

take(n) yields the first n items. skip(n) skips the first n items. Both are lazy.

let first_three: Vec<i32> = (1..).take(3).collect();
assert_eq!(first_three, vec![1, 2, 3]);

let from_four: Vec<i32> = (1..).skip(3).take(3).collect();
assert_eq!(from_four, vec![4, 5, 6]);

take on an infinite iterator is the standard way to bound it.

take_while and skip_while

take_while(pred) yields items while the predicate is true. skip_while(pred) skips items while the predicate is true.

let under_ten: Vec<i32> = (1..).take_while(|&x| x < 10).collect();
assert_eq!(under_ten, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);

Both stop examining as soon as the predicate flips, and both can operate on infinite iterators.

enumerate

enumerate pairs each item with its index.

let indexed: Vec<(usize, &str)> = vec!["a", "b", "c"].iter().enumerate().collect();
assert_eq!(indexed, vec![(0, &"a"), (1, &"b"), (2, &"c")]);

The index starts at zero.

rev

rev reverses a double-ended iterator.

let reversed: Vec<i32> = vec![1, 2, 3].into_iter().rev().collect();
assert_eq!(reversed, vec![3, 2, 1]);

rev requires the underlying iterator to implement DoubleEndedIterator.

peekable

peekable wraps the iterator and adds a peek method that returns the next item without consuming it.

let mut iter = vec![1, 2, 3].into_iter().peekable();
assert_eq!(iter.peek(), Some(&1));
assert_eq!(iter.next(), Some(1));

peek is useful when the decision to consume depends on the next item.

The adapter table

AdapterInputOutputNotes
mapFnMut(Item) -> UUOne-to-one
filterFnMut(&Item) -> boolItemDrops items
filter_mapFnMut(Item) -> Option<U>UDrops None
flat_mapFnMut(Item) -> IntoIteratorUFlattens
chainIntoIteratorItemConcatenates
zipIntoIterator(Item, U)Pairs
takeusizeItemFirst n
skipusizeItemDrops first n
take_whileFnMut(&Item) -> boolItemPrefix
skip_whileFnMut(&Item) -> boolItemDrops prefix
enumerate—(usize, Item)Index
rev—ItemReverse
peekable—ItemLook-ahead

c. Consumers

A consumer drives the iterator and produces a final value. Consumers are eager: they pull items until the iterator is exhausted or until a condition stops them.

collect

collect gathers the items into a collection. The target type is inferred from context or specified with turbofish.

let v: Vec<i32> = (1..=5).collect();
let s: String = "hello".chars().collect();
let set: std::collections::HashSet<i32> = vec![1, 1, 2, 3].into_iter().collect();

collect can target any type that implements FromIterator.

fold

fold accumulates a value by applying a function to each item and the accumulator.

let sum = (1..=5).fold(0, |acc, x| acc + x);
assert_eq!(sum, 15);

The initial value is the first argument. The closure receives the accumulator and the item, and returns the new accumulator.

reduce

reduce is like fold but uses the first item as the initial accumulator.

let product = (1..=5).reduce(|acc, x| acc * x);
assert_eq!(product, Some(120));

reduce returns Option because an empty iterator has no initial value.

for_each

for_each calls a closure for each item, for side effects.

vec![1, 2, 3].iter().for_each(|x| println!("{}", x));

for_each is the iterator equivalent of a for loop.

sum and product

sum and product accumulate numeric items.

let total: i32 = (1..=5).sum();
let factorial: i32 = (1..=5).product();
assert_eq!(total, 15);
assert_eq!(factorial, 120);

Both require the item type to implement Sum or Product.

count

count returns the number of items.

let n = (1..=5).count();
assert_eq!(n, 5);

count consumes the iterator.

any and all

any(pred) returns true if any item matches. all(pred) returns true if all items match.

assert!((1..=5).any(|x| x == 3));
assert!((1..=5).all(|x| x > 0));

Both short-circuit: any stops at the first match, all stops at the first mismatch.

find

find(pred) returns the first matching item.

let found = (1..=5).find(|&x| x > 3);
assert_eq!(found, Some(4));

position

position(pred) returns the index of the first matching item.

let pos = vec![10, 20, 30].iter().position(|&x| x == 20);
assert_eq!(pos, Some(1));

min, max, min_by, max_by

min and max return the smallest and largest items. min_by and max_by use a comparison function.

let smallest = vec![3, 1, 4, 1, 5].into_iter().min();
assert_eq!(smallest, Some(1));

let longest = vec!["a", "abc", "ab"].into_iter().max_by_key(|s| s.len());
assert_eq!(longest, Some("abc"));

The consumer table

ConsumerReturnsShort-circuits
collectA collectionNo
foldAccumulatorNo
reduceOption<Accumulator>No
for_each()No
sumNumericNo
productNumericNo
countusizeNo
anyboolYes
allboolYes
findOption<Item>Yes
positionOption<usize>Yes
min / maxOption<Item>No

Complete Example Session

// ============================================
// PART 1: THE NEXT METHOD
// ============================================

fn main() {
    let mut iter = vec![1, 2, 3].into_iter();
    assert_eq!(iter.next(), Some(1));
    assert_eq!(iter.next(), Some(2));
    assert_eq!(iter.next(), Some(3));
    assert_eq!(iter.next(), None);
}

// ============================================
// PART 2: THE THREE ITERATION FORMS
// ============================================

fn main() {
    let mut v = vec![1, 2, 3];

    for x in &v { /* x: &i32 */ }
    for x in &mut v { /* x: &mut i32 */ }
    for x in v { /* x: i32; v is consumed */ }
}

// ============================================
// PART 3: MAP AND FILTER
// ============================================

fn main() {
    let result: Vec<i32> = (1..=10)
        .filter(|x| x % 2 == 0)
        .map(|x| x * x)
        .collect();
    assert_eq!(result, vec![4, 16, 36, 64, 100]);
}

// ============================================
// PART 4: LAZINESS
// ============================================

fn main() {
    // This chain does nothing.
    (1..=1000000).map(|x| x * 2).filter(|x| x % 3 == 0);

    // The compiler warns: unused iterator.
    // No work happens until a consumer runs.

    // Adding a consumer drives the chain.
    let count = (1..=1000000)
        .map(|x| x * 2)
        .filter(|x| x % 3 == 0)
        .count();
    println!("{}", count);
}

// ============================================
// PART 5: INFINITE ITERATORS
// ============================================

fn main() {
    let first_five: Vec<i32> = (1..)
        .map(|x| x * x)
        .take(5)
        .collect();
    assert_eq!(first_five, vec![1, 4, 9, 16, 25]);
}

// ============================================
// PART 6: FLAT_MAP
// ============================================

fn main() {
    let words: Vec<&str> = vec!["hello world", "foo bar baz"]
        .into_iter()
        .flat_map(|s| s.split_whitespace())
        .collect();
    assert_eq!(words, vec!["hello", "world", "foo", "bar", "baz"]);
}

// ============================================
// PART 7: CONSUMERS — FOLD AND COLLECT
// ============================================

fn main() {
    let sum: i32 = (1..=5).fold(0, |acc, x| acc + x);
    assert_eq!(sum, 15);

    let v: Vec<i32> = (1..=5).collect();
    assert_eq!(v, vec![1, 2, 3, 4, 5]);
}

// ============================================
// PART 8: SHORT-CIRCUITING CONSUMERS
// ============================================

fn main() {
    assert!((1..=100).any(|x| x == 50));
    assert!((1..=5).all(|x| x > 0));
    assert_eq!((1..=100).find(|&x| x > 90), Some(91));
    assert_eq!(vec![10, 20, 30].iter().position(|&x| x == 20), Some(1));
}

// ============================================
// PART 9: ZIP AND ENUMERATE
// ============================================

fn main() {
    let pairs: Vec<(i32, &str)> = vec![1, 2, 3].into_iter()
        .zip(vec!["a", "b", "c"])
        .collect();
    assert_eq!(pairs, vec![(1, "a"), (2, "b"), (3, "c")]);

    let indexed: Vec<(usize, i32)> = vec![10, 20, 30].into_iter()
        .enumerate()
        .collect();
    assert_eq!(indexed, vec![(0, 10), (1, 20), (2, 30)]);
}

// ============================================
// PART 10: THE COMPLETE CHAIN
// ============================================

fn main() {
    let result: i32 = (1..=100)
        .filter(|x| x % 3 == 0)
        .map(|x| x * x)
        .take(5)
        .sum();
    // 3² + 6² + 9² + 12² + 15² = 9 + 36 + 81 + 144 + 225 = 495
    assert_eq!(result, 495);
}

The ten parts progressed from next through the three iteration forms, map and filter, laziness, infinite iterators, flat_map, fold and collect, short-circuiting consumers, zip and enumerate, and the complete chain. The same collection and range examples ran through every part, showing how adapters and consumers combine.


Quick Reference

The Iterator Trait

ItemDescription
type ItemThe element type
fn next(&mut self) -> Option<Item>The only required method
Provided methodsOver seventy, all in terms of next

Adapters

AdapterPurpose
mapTransform each item
filterKeep items matching a predicate
filter_mapTransform and drop None
flat_mapTransform to iterators and flatten
chainConcatenate two iterators
zipPair two iterators
take / skipLimit or drop first n
take_while / skip_whileLimit or drop a prefix
enumerateAdd an index
revReverse
peekableLook ahead

Consumers

ConsumerReturns
collectA collection
foldAccumulator
reduceOption<Accumulator>
for_each()
sum / productNumeric
countusize
any / allbool (short-circuits)
findOption<Item>
positionOption<usize>
min / maxOption<Item>

Laziness Rules

RuleConsequence
Adapters are lazyNo work until a consumer runs
Consumers are eagerThey pull until exhaustion or short-circuit
Chains need a consumerOtherwise nothing happens
Infinite iterators are fineIf bounded by take or take_while

Best Practices

✅ Do This:

// Chain adapters and end with a consumer.
let v: Vec<i32> = (1..=10).filter(|x| x % 2 == 0).map(|x| x * x).collect();  // ✅

// Use take on infinite iterators.
let first_five: Vec<i32> = (1..).take(5).collect();  // ✅

// Use short-circuiting consumers when possible.
let has_negative = v.iter().any(|&x| x < 0);  // ✅

// Use fold for custom accumulations.
let sum = v.iter().fold(0, |acc, &x| acc + x);  // ✅

// Use filter_map to combine filter and map.
let parsed: Vec<i32> = strings.iter().filter_map(|s| s.parse().ok()).collect();  // ✅

// Use enumerate instead of a manual index.
for (i, x) in v.iter().enumerate() { }  // ✅

❌ Don’t Do This:

// Don't create an adapter chain without a consumer.
(1..=100).map(|x| x * 2);  // ❌ Does nothing

// Don't collect when you only need a count.
let count = v.iter().filter(...).collect::<Vec<_>>().len();  // ❌ Use count()

// Don't use map for side effects.
v.iter().map(|x| println!("{}", x)).collect::<Vec<_>>();  // ❌ Use for_each

// Don't clone just to iterate.
for x in v.clone() { }  // ❌ Use &v or into_iter

// Don't mix up iter and into_iter.
for x in v.iter() { /* x: &T */ }  // ✅
for x in v.into_iter() { /* x: T */ }  // ✅

// Don't assume an adapter consumes the iterator.
let doubled = v.iter().map(|x| x * 2);  // ❌ Still lazy; needs consumer

Common Pitfalls

PitfallWhy It HappensFix
Chain does nothingNo consumerAdd collect, sum, for_each, etc.
Iterator unused warningAdapter chain not consumedAdd a consumer or remove the chain
Cannot use iterator twiceIterator consumedRecreate it or collect first
Wrong item typeWrong iteration formChoose iter, iter_mut, into_iter
Infinite loopInfinite iterator without takeBound with take or take_while
Double borrowiter_mut while iteratingRestructure the loop
collect type ambiguousNo target typeAnnotate with turbofish or a binding
Borrow checker error in closureCapturing by referenceUse move or restructure

Real-World Examples

1. Sum of Squares

let total: i32 = (1..=10).map(|x| x * x).sum();

2. Even Numbers

let evens: Vec<i32> = (1..=10).filter(|x| x % 2 == 0).collect();

3. Parse and Filter

let parsed: Vec<i32> = strings.iter().filter_map(|s| s.parse().ok()).collect();

4. Flatten Nested

let flat: Vec<i32> = nested.into_iter().flat_map(|v| v).collect();

5. Find First Match

let found = items.iter().find(|&&x| x > 10);

6. Check Any

let has_error = logs.iter().any(|l| l.contains("ERROR"));

7. Enumerate with Index

for (i, item) in items.iter().enumerate() { }

8. Zip Two Lists

let pairs: Vec<_> = names.into_iter().zip(ages).collect();

9. Fold to Build a Map

let map = pairs.fold(HashMap::new(), |mut acc, (k, v)| {
    acc.insert(k, v);
    acc
});

10. Take from an Infinite Range

let first_ten: Vec<u32> = (1..).take(10).collect();

Visual

Adapters vs Consumers

┌────────────────────────────────────────────────────────────┐
│  ADAPTERS vs CONSUMERS                                     │
│                                                            │
│  ADAPTERS (lazy)                                           │
│  ┌──────────────────────────────────────────────────┐     │
│  │  map      → Iterator                              │     │
│  │  filter   → Iterator                              │     │
│  │  take     → Iterator                              │     │
│  │  chain    → Iterator                              │     │
│  │  zip      → Iterator                              │     │
│  │  enumerate → Iterator                             │     │
│  └──────────────────────────────────────────────────┘     │
│                        │                                   │
│                        │  Build a pipeline                │
│                        v                                   │
│  ┌──────────────────────────────────────────────────┐     │
│  │  CONSUMERS (eager)                                │     │
│  │  collect  → Collection                            │     │
│  │  fold     → Accumulator                           │     │
│  │  sum      → Number                                │     │
│  │  any/all  → bool (short-circuit)                  │     │
│  │  find     → Option                                │     │
│  │  count    → usize                                 │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  Adapters return iterators. Consumers return final values. │
│  A chain needs exactly one consumer at the end.            │
│                                                            │
└────────────────────────────────────────────────────────────┘

The Lazy Pull Model

┌────────────────────────────────────────────────────────────┐
│  LAZY PULL MODEL                                           │
│                                                            │
│  Consumer pulls one item:                                  │
│                                                            │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐              │
│  │  Source  │──▶│  map     │──▶│  filter  │──▶ Consumer  │
│  │  (range) │   │          │   │          │              │
│  └──────────┘   └──────────┘   └──────────┘              │
│       ▲              ▲              ▲                     │
│       │              │              │                     │
│       └──────────────┴──────────────┘                     │
│              One next() call propagates                    │
│              through the chain.                            │
│                                                            │
│  The source yields one item.                               │
│  map transforms it.                                        │
│  filter checks it.                                         │
│  Consumer receives it or pulls again.                      │
│                                                            │
│  No intermediate collection. No wasted work.               │
│                                                            │
└────────────────────────────────────────────────────────────┘

Infinite Iterators

┌────────────────────────────────────────────────────────────┐
│  INFINITE ITERATOR BOUNDED BY take                         │
│                                                            │
│  (1..)                  Infinite source                    │
│    │                                                       │
│    ▼                                                       │
│  .map(|x| x * 2)        Lazy, still infinite               │
│    │                                                       │
│    ▼                                                       │
│  .take(5)               Bounds to 5 items                  │
│    │                                                       │
│    ▼                                                       │
│  .collect()             Produces [2, 4, 6, 8, 10]         │
│                                                            │
│  The source would run forever without take.                │
│  take stops pulling after 5 items.                         │
│  The chain terminates normally.                            │
│                                                            │
└────────────────────────────────────────────────────────────┘

Short-Circuiting

┌────────────────────────────────────────────────────────────┐
│  SHORT-CIRCUITING CONSUMERS                                │
│                                                            │
│  any(|x| x == 3) on [1, 2, 3, 4, 5]                       │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Check 1 → false                                   │     │
│  │  Check 2 → false                                   │     │
│  │  Check 3 → true  → STOP                            │     │
│  │  Items 4 and 5 are never examined.                 │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  all(|x| x > 0) on [1, 2, -1, 4, 5]                       │
│  ┌──────────────────────────────────────────────────┐     │
│  │  Check 1 → true                                    │     │
│  │  Check 2 → true                                    │     │
│  │  Check -1 → false → STOP                           │     │
│  │  Items 4 and 5 are never examined.                 │     │
│  └──────────────────────────────────────────────────┘     │
│                                                            │
│  find and position also short-circuit.                     │
│  Short-circuiting saves work on large or infinite sources. │
│                                                            │
└────────────────────────────────────────────────────────────┘

Summary

ItemValue
Iterator traitDefines next and over seventy provided methods
type ItemThe element type
nextReturns Some(item) or None
AdapterReturns a new iterator; lazy
ConsumerDrives the iterator; eager
IntoIteratorTrait for types usable in for loops
iter()Borrows shared; items are &T
iter_mut()Borrows mutable; items are &mut T
into_iter()Consumes; items are T
Infinite iteratorBounded by take or take_while
Short-circuitingany, all, find, position

Key takeaways:

  • The Iterator trait has one required method, next. Everything else — every adapter and every consumer — is provided in terms of next. Implementing next for a custom type gives access to the entire adapter and consumer vocabulary.
  • Adapters are lazy. Calling map, filter, or take does no work. The work happens when a consumer pulls from the resulting iterator. A chain of adapters without a consumer compiles but does nothing.
  • Consumers are eager. collect, fold, sum, for_each, and the rest pull items until the iterator is exhausted or a condition short-circuits the pull.
  • Every chain ends in exactly one consumer. The consumer determines the final type. The adapters determine the transformation.
  • Laziness enables infinite iterators. (1..) is infinite, but combined with take(5) it produces exactly five items. The take adapter stops pulling after the limit, so the source is never exhausted.
  • Laziness enables short-circuiting. any stops at the first match, all stops at the first mismatch, find stops at the first found item. Each saves work on large inputs.
  • The three iteration forms matter. iter() yields shared references, iter_mut() yields mutable references, and into_iter() yields owned values. The choice determines what closures can do with each item.
  • Iterator chains compile to hand-written loops. The compiler inlines the adapter calls and the consumer’s loop, eliminating the abstraction overhead. There is no per-element allocation and no dynamic dispatch unless a trait object is used.

Remember: Rust’s iterator API is organized around a single distinction: adapters are lazy, consumers are eager. An adapter takes an iterator and returns a new iterator; it does no work until a consumer pulls from the chain. A consumer drives the chain to completion and produces a final value. Every chain ends in a consumer, and a chain without one does nothing. The laziness is what makes infinite iterators possible and short-circuiting effective. The three iteration forms — iter, iter_mut, and into_iter — determine whether the closures see references, mutable references, or owned values. The whole API is built on one method, next, and everything else follows from it. The result is a pipeline that reads like a description of the transformation and compiles to code as efficient as a hand-written loop.


Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!