Skip to content

Iterators (std::Iter)

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().

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.

01-range.zolo
Playground
// 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.

02-map-filter.zolo
Playground
// 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.

03-fold.zolo
Playground
// 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.

04-take-skip.zolo
Playground
// 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.

05-zip.zolo
Playground
// 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.

06-collect-each.zolo
Playground
// 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]

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?

enespt-br