Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Produce values incrementally

A generator function declares Generator[T] and uses yield statements:

def countdown(start: i64) -> Generator[i64]:
    mut remaining = start
    while remaining > 0:
        yield remaining
        # The loop proves 1 <= remaining <= i64.MAX.
        unchecked:
            remaining -= 1

def main() -> Result[(), IoError]:
    numbers = countdown(3)
    print("created")?
    for number in numbers:
        print(number)?
    Ok(())
created
3
2
1

Calling a generator evaluates its arguments immediately but starts its body only when iteration requests a value. Each yield pauses the body, retaining its locals until the next request. A bare return or the end of the body finishes it; a generator cannot return a value.

Creating the generator needs no allocation or ?. Allocating arguments and operations in its body still need their usual error handling. A generator cannot propagate arithmetic errors to its caller. The countdown uses one small unchecked: suite: the loop proves that subtracting one stays in range. For unbounded calculations, yield Result items and let the consumer handle them, as in lazy expressions.

Comprehensions and collection constructors also consume generators:

def numbers(limit: i64) -> Generator[i64]:
    for n in range(limit):
        yield n

def main() -> Result[(), AllocError | IoError | ZeroDivisionError | OverflowError[i64]]:
    print([n * n for n in numbers(6) if n % 2 == 1]?)?
    print(list(numbers(3))?)?
    Ok(())
[1, 9, 25]
[0, 1, 2]

Call next(&mut messages) to advance a mutable generator without consuming it. The shorthand next(messages) also works. It returns Some(value) or Nothing; an exhausted generator keeps returning Nothing:

def once() -> Generator[str]:
    yield "hello"

def main() -> Result[(), IoError]:
    mut messages = once()
    print(next(&mut messages))?
    print(next(&mut messages))?
    print(next(&mut messages))?
    Ok(())
Option[str].Some("hello")
Option[str].Nothing
Option[str].Nothing

Breaking out of consuming iteration drops the generator without resuming its body. Yielding an owned value transfers ownership; to keep it, handle copy(value)’s result and yield the copy.

Generators cannot yield other generators, references, or closures. For a lazy expression producer, use a generator expression. yield from, send, and async operations are unsupported.