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

Build collections with comprehensions

A comprehension produces a new collection from an iterable and optional filters. The element expression runs only after the filters pass.

def main() -> Result[(), AllocError | IoError | ZeroDivisionError | OverflowError[i64]]:
    squares = [n * n for n in range(8) if n % 2 == 0]?
    print(squares)?
    print({n: n * n for n in [2, 3]?}?)?
    print(len({n // 2 for n in range(8)}?))?
    print([x * 10 + y for x in range(3) for y in range(x)]?)?
    Ok(())
[0, 4, 16, 36]
{2: 4, 3: 9}
4
[10, 20, 21]

Multiple clauses nest from left to right. Later iterables can use earlier variables, and you can use multiple if filters. Variables remain local to the comprehension. Parenthesize a conditional expression used as an iterable or filter. Dictionary comprehensions evaluate each key before its value.

% is modulo: the result follows the divisor’s sign, so -7 % 3 is 2. Both operands must have the same integer type.

Every element must have the same type; there is no implicit numeric widening:

def main() -> Result[(), AllocError | IoError]:
    values = [1, True]?
    Ok(())
expected list[i64]

A comprehension over a range or list with no if clause reserves its full length once; with a filter or another source it grows the output in place. The ? handles allocation failure. A comprehension with one for clause compiles to the same loop as the equivalent pipeline.

Collect a snapshot with unpacking

Unpacking works in comprehensions too. This one collects dictionary items into a list of tuples:

def main() -> Result[(), AllocError | IoError | OverflowError[i64]]:
    mut counts = {"apple": 2, "pear": 3}?
    for fruit, count in (&mut counts).items():
        *count = *count + 1
    print([(*fruit, *count) for fruit, count in counts.items()]?)?
    print([a + b for a, b in [(1, 2), (3, 4)]?]?)?
    Ok(())
[("apple", 3), ("pear", 4)]
[3, 7]

For dictionaries containing owned values, use copy(value)? when the snapshot needs independent owners. Shared item views yield references for every key and value, so the * above reads each copyable value before storing it in the snapshot tuple.

Text comprehensions collect Unicode scalars:

def main() -> Result[(), AllocError | IoError]:
    print([character for character in "hé"]?)?
    Ok(())
["h", "é"]