Rust 52 🦀 Iterator Trait and Consumers vs Adapters
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
| Form | Item type | Ownership |
|---|---|---|
into_iter() | T | Consumes the collection |
iter() | &T | Borrows shared |
iter_mut() | &mut T | Borrows 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
| Adapter | Input | Output | Notes |
|---|---|---|---|
map | FnMut(Item) -> U | U | One-to-one |
filter | FnMut(&Item) -> bool | Item | Drops items |
filter_map | FnMut(Item) -> Option<U> | U | Drops None |
flat_map | FnMut(Item) -> IntoIterator | U | Flattens |
chain | IntoIterator | Item | Concatenates |
zip | IntoIterator | (Item, U) | Pairs |
take | usize | Item | First n |
skip | usize | Item | Drops first n |
take_while | FnMut(&Item) -> bool | Item | Prefix |
skip_while | FnMut(&Item) -> bool | Item | Drops prefix |
enumerate | — | (usize, Item) | Index |
rev | — | Item | Reverse |
peekable | — | Item | Look-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
| Consumer | Returns | Short-circuits |
|---|---|---|
collect | A collection | No |
fold | Accumulator | No |
reduce | Option<Accumulator> | No |
for_each | () | No |
sum | Numeric | No |
product | Numeric | No |
count | usize | No |
any | bool | Yes |
all | bool | Yes |
find | Option<Item> | Yes |
position | Option<usize> | Yes |
min / max | Option<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
| Item | Description |
|---|---|
type Item | The element type |
fn next(&mut self) -> Option<Item> | The only required method |
| Provided methods | Over seventy, all in terms of next |
Adapters
| Adapter | Purpose |
|---|---|
map | Transform each item |
filter | Keep items matching a predicate |
filter_map | Transform and drop None |
flat_map | Transform to iterators and flatten |
chain | Concatenate two iterators |
zip | Pair two iterators |
take / skip | Limit or drop first n |
take_while / skip_while | Limit or drop a prefix |
enumerate | Add an index |
rev | Reverse |
peekable | Look ahead |
Consumers
| Consumer | Returns |
|---|---|
collect | A collection |
fold | Accumulator |
reduce | Option<Accumulator> |
for_each | () |
sum / product | Numeric |
count | usize |
any / all | bool (short-circuits) |
find | Option<Item> |
position | Option<usize> |
min / max | Option<Item> |
Laziness Rules
| Rule | Consequence |
|---|---|
| Adapters are lazy | No work until a consumer runs |
| Consumers are eager | They pull until exhaustion or short-circuit |
| Chains need a consumer | Otherwise nothing happens |
| Infinite iterators are fine | If 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
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Chain does nothing | No consumer | Add collect, sum, for_each, etc. |
Iterator unused warning | Adapter chain not consumed | Add a consumer or remove the chain |
| Cannot use iterator twice | Iterator consumed | Recreate it or collect first |
| Wrong item type | Wrong iteration form | Choose iter, iter_mut, into_iter |
| Infinite loop | Infinite iterator without take | Bound with take or take_while |
| Double borrow | iter_mut while iterating | Restructure the loop |
collect type ambiguous | No target type | Annotate with turbofish or a binding |
| Borrow checker error in closure | Capturing by reference | Use 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
| Item | Value |
|---|---|
Iterator trait | Defines next and over seventy provided methods |
type Item | The element type |
next | Returns Some(item) or None |
| Adapter | Returns a new iterator; lazy |
| Consumer | Drives the iterator; eager |
IntoIterator | Trait 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 iterator | Bounded by take or take_while |
| Short-circuiting | any, all, find, position |
Key takeaways:
- The
Iteratortrait has one required method,next. Everything else — every adapter and every consumer — is provided in terms ofnext. Implementingnextfor a custom type gives access to the entire adapter and consumer vocabulary. - Adapters are lazy. Calling
map,filter, ortakedoes 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 withtake(5)it produces exactly five items. Thetakeadapter stops pulling after the limit, so the source is never exhausted. - Laziness enables short-circuiting.
anystops at the first match,allstops at the first mismatch,findstops 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, andinto_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.
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!