27 · Standard Library
Iterators (std::Iter)
Lazy sequences: range, map, filter, fold, take, skip, zip, collect and each.
On this page
std::Iter provides lazy pipelines — each step only processes an item
when the next step requests it. No intermediate array is allocated until you
call collect() or each().
Optional values and the end of iteration
Iter.from(array), map, filter and collect preserve optional elements.
A callback returning nil from map produces a present item; it does not
end the pipeline. A for loop visits that item and continues with the next one.
Iter.from_fn(callback) keeps its existing sentinel contract: nil returned
by the source callback ends the sequence. The same convention applies to a
custom iterator's legacy next() method. To process missing values, start
from an array or produce them in a map after the source.
Range
Iter::range(start, end) produces the integers [start, end) without
materialising the array. It is the most common source for a pipeline:
Exclusive numeric sequence at the end; collect() materialises the array.
// Feature: Iter.range — lazy numeric sequence
// When to use: producing a source for a pipeline without materializing an array.
use std::Iter
// Simple range 0..5 (end exclusive).
let r = Iter::range(0, 5)
print(r.collect())
// expected: [0, 1, 2, 3, 4]
// Empty range when start >= end.
let empty = Iter::range(5, 5)
print(empty.collect())
// expected: []
// Sum from 1 to 10.
let sum = Iter::range(1, 11).fold(0, |acc, x| acc + x)
print(sum)
// expected: 55
// Range feeding map.
let squares = Iter::range(1, 6).map(|x| x * x).collect()
print(squares)
// expected: [1, 4, 9, 16, 25]
Map and Filter
map transforms each item; filter discards those that do not satisfy the
predicate. Chained together they form a linear-reading pipeline:
filter -> map -> collect with no intermediate arrays.
// Feature: Iter.map / Iter.filter — lazy transform and filter
// When to use: processing pipelines without allocating intermediate arrays.
use std::Iter
// map — transforms each item.
let doubled = Iter::range(1, 6).map(|x| x * 2).collect()
print(doubled)
// expected: [2, 4, 6, 8, 10]
// filter — keeps items that pass the predicate.
let evens = Iter::range(1, 11).filter(|x| x % 2 == 0).collect()
print(evens)
// expected: [2, 4, 6, 8, 10]
// Pipeline: filter -> map -> collect.
let it = Iter::range(1, 11)
let only_even = it.filter(|x| x % 2 == 0)
let squared = only_even.map(|x| x * x)
print(squared.collect())
// expected: [4, 16, 36, 64, 100]
Fold
fold(acc, |acc, x| ...) reduces the whole sequence to a single value — sum,
product, concatenated string:
Sum, factorial and sum of even squares with the same combinator.
// Feature: Iter.fold — reduce an iterator to a single value
// When to use: summing, multiplying, building a final string, counting.
use std::Iter
// Sum 1..10.
let sum = Iter::range(1, 11).fold(0, |acc, x| acc + x)
print(sum) // expected: 55
// Product 1..6 (= 5!).
let fact = Iter::range(1, 6).fold(1, |acc, x| acc * x)
print(fact) // expected: 120
// Sum of even squares from 1..10.
let it = Iter::range(1, 11)
let it2 = it.map(|x| x * x)
let it3 = it2.filter(|x| x % 2 == 0)
let result = it3.fold(0, |acc, x| acc + x)
print(result) // expected: 220
// String concatenation.
let nums = Iter::range(1, 4)
let joined = nums.fold("", |acc, x| acc + "{x};")
print(joined)
// expected: 1;2;3;
Take and Skip
take(n) limits how many items flow forward; skip(n) ignores the first ones.
Together they implement pagination on any iterator:
skip(1).take(3) — skips the header and takes the next three lines.
// Feature: Iter.take / Iter.skip — limit and skip elements
// When to use: pagination, taking first N, ignoring a stream header.
use std::Iter
// take — grabs the first N.
let first3 = Iter::range(0, 100).take(3).collect()
print(first3)
// expected: [0, 1, 2]
// skip — drops the first N.
let skip5 = Iter::range(0, 20).skip(5).take(4).collect()
print(skip5)
// expected: [5, 6, 7, 8]
// take beyond the length — stops at the end.
let small = Iter::range(0, 3).take(100).collect()
print(small)
// expected: [0, 1, 2]
// Skip the header of a "stream" (range simulating lines).
let lines = Iter::range(1, 11) // 10 items
let body = lines.skip(1) // ignore the "header"
let preview = body.take(3)
print(preview.collect())
// expected: [2, 3, 4]
Zip
zip combines two iterators into [a, b] pairs, stopping at the shorter one:
Value pairs and sum mapping with zip -> map.
// Feature: Iter.zip — combines two iterators into pairs
// When to use: pairing index/value, headers with columns, etc.
use std::Iter
// Simple zip — stops at the shorter side.
let a = Iter::range(1, 4)
let b = Iter::range(10, 14)
let pairs = a.zip(b).collect()
// Each pair is an [x, y] array.
print(pairs.len()) // expected: 3
print(pairs[0]) // expected: [1, 10]
print(pairs[1]) // expected: [2, 11]
print(pairs[2]) // expected: [3, 12]
// Zip stops at the shorter side — 3 items, not 5.
let short = Iter::range(0, 3)
let long = Iter::range(0, 5)
print(short.zip(long).collect().len())
// expected: 3
// Combine with map to sum pairs.
let xs = Iter::range(1, 5)
let ys = Iter::range(10, 14)
let zipped = xs.zip(ys)
let sums = zipped.map(|p| p[0] + p[1])
print(sums.collect())
// expected: [11, 13, 15, 17]
Collect and Each
collect() terminates the pipeline and returns an array. each() executes a
side effect per item and returns nil — useful for printing or accumulating
into an external variable:
collect returns an array; each is for effects — it returns no value.
// Feature: Iter.collect / Iter.each — terminate a pipeline
// When to use: collect when you need the array; each for side effects.
use std::Iter
// collect — materializes the iterator into an array.
let arr = Iter::range(1, 5).collect()
print(arr)
// expected: [1, 2, 3, 4]
print(arr.len()) // expected: 4
// each — runs a side effect per item, returns nil.
print("--- each ---")
Iter::range(1, 4).each(|x| print("item={x}"))
// expected: --- each ---
// expected: item=1
// expected: item=2
// expected: item=3
// Full pipeline: range -> filter -> map -> collect.
let it1 = Iter::range(1, 11)
let it2 = it1.filter(|x| x % 2 == 1) // odd numbers
let it3 = it2.map(|x| x * 10)
print(it3.collect())
// expected: [10, 30, 50, 70, 90]
Backend behavior
The wasm-aot backend still evaluates array-backed Iter.from pipelines
eagerly. Optional slots are preserved there too. Its from_fn pipelines
support map, filter, collect and for; unsupported adapters on that
source fail explicitly. Full lazy-iterator parity remains separate work.
Challenge
Build a pipeline that produces the first 5 odd numbers greater than 10 using
range, filter, take and collect. How many calls to filter are
executed?
See also