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

Comprehensions and collection construction

List, set, and dictionary comprehensions return Result[collection, AllocError]. Multiple for and if clauses nest from left to right, each one a level of nesting. Each iterable evaluates when its enclosing iteration reaches it; filters run before the result expression. Dictionary keys evaluate before their values. Iteration uses the same consuming or borrowed contracts as loops.

def main() -> Result[(), AllocError | IoError | OverflowError[i64]]:
    values = [n * n for n in range(5) if n > 1]?
    print(values)?
    Ok(())
[4, 9, 16]

A comprehension with one for clause and any number of if clauses lowers to the same loop as the equivalent pipeline: [f(x) for x in s if p(x)] runs as iter(s).filter(p).map(f).collect() would, with no hidden function. Its iterable is evaluated before the output is allocated. Over a known-length range or list without filters, the output reserves its full length once and never grows. Comprehensions with several for clauses keep the nested loop.

Comprehension bindings have their own scope; the first iterable sees the enclosing scope. Flat tuple targets are supported. Conditional expressions in iterables or filters must be parenthesized. Parenthesized generator expressions produce lazy owned frames instead of collection Results. Async comprehensions are unsupported.

Output ownership and growth are fallible. On growth failure, the partial output, current yielded owners, and active hidden iterators clean up. No iterator advances and no later filter or element expression runs. Nested expressions keep their own Result types and failure behavior. A nested ? propagates from the enclosing function rather than becoming an error inside the surrounding comprehension Result. Already performed side effects are not rolled back.

Collection displays [elements], {elements}, and {key: value} follow the same recoverable output-allocation policy. Their owner allocates before entries evaluate left to right; allocation failure prevents later entry evaluation. Expected Result types supply element types for empty displays. See collection construction for literal types and duplicate-entry behavior.

list[T].from(source) and set[T].from(source) consume an owned collection, range, or generator and return Result with AllocError. Failure destroys partial output, the current element, and remaining source. Generator effects before failure persist. Sets require hashable elements and keep the first occurrence of each value. Borrowed sources and strings are unsupported by these .from(...) methods. The call-style constructors additionally accept strings and bytes; see collection conversion.

A directly written range in a comprehension can receive numeric element context from the output annotation through direct arithmetic, for example squares: list[u8] = [n * n for n in range(8)]?. This inference does not run backward through arbitrary calls, filters, stored ranges, or explicit range type arguments. Use range[u8](...) at those boundaries. Fixed-width arithmetic follows its normal overflow rules.