Skip to content

Iteration

Traverse iterable values and compose one-shot iterator adapters.

Updated View as Markdown

for value in source uses the Iterable[T] protocol. The compiler asks the source for an Iterator[T] and repeatedly calls next() until it returns None.

for name in names {
    println(name)
}

Lists, strings, maps, sets, slices, and ranges provide iterable behavior.

Iteration does not imply indexed access. Write algorithms against Iterable[T] when they only need sequential values; require a concrete list or slice when they need indexing, length, or mutation.

Patterns

Loop bindings are patterns:

for index, value in values.enumerate() {
    println("$index: $value")
}

for key, value in settings {
    apply(key, value)
}

Refutable loop patterns use the dedicated for pattern := expression form described in Loops.

Iterators

Iterator[T] is one-shot:

trait Iterator[T] {
    fn next(mut self): Option[T]
}

An iterator is a stateful, one-shot cursor. Calling next() advances it. Default consuming operations are:

Method Consumption
for_each(f) Visits the remainder
count() Counts and exhausts the remainder
first() Consumes at most one item
last() Exhausts and returns the final item
any(f), all(f) Stop once the answer is known
find(f) Stops at the first match

After a short-circuiting call, the cursor remains positioned after the item that decided the result. Do not expect a consumed iterator to restart.

Lazy adapters return new iterators:

selected := source.iterator()
    .filter(value => value.active)
    .map(value => value.id)
    .take(10)

The current generic adapter set is deliberately small:

Adapter Behavior
map(f) Transform each pulled item
filter(f) Pull until an item passes
take(n) Yield at most n items
skip(n) Discard the first n items

Adapters are lazy: constructing the chain does not visit the source. Work happens when next() or a consuming method pulls values. A non-positive take yields nothing; a non-positive skip skips nothing.

Collection methods with similar names often allocate eager lists. For example, values.map(f) creates a list, whereas values.iterator().map(f) creates a cursor. Choose based on whether the result must be stored or only traversed.

Iterable

Iterable[T] represents a repeatable source that can create an iterator. Its associated iterator type is compiler-resolved:

trait Iterable[T] {
    type Iter: Iterator[T]
    fn iterator(self): Iter
}

Implement Iterator for a stateful cursor and Iterable for the collection that creates it. Avoid returning the same mutable cursor from repeated iterator() calls.

Iterable also supplies eager for_each_item, count_items, and first_item defaults. Each obtains a fresh iterator, so repeatable collections can call them independently.

struct CountdownIter {
    remaining: int
}

impl Iterator[int] for CountdownIter {
    fn next(mut self): int? {
        if self.remaining <= 0 {
            return None
        }
        self.remaining -= 1
        return Some(self.remaining)
    }
}

The iterator owns its progress. The collection’s iterator() method should construct that progress state rather than mutate the collection itself.

Ranges

start..end is half-open; start..=end is inclusive. Integer ranges integrate with for:

for index in 0..items.len() {
    use(items.get(index))
}

Prefer direct collection iteration when an index is not otherwise needed.

Half-open ranges compose naturally with zero-based lengths because the end is excluded. Inclusive ranges are useful for closed numeric intervals. Range syntax and the four for forms are specified in Loops.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close