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

Ranges

range(stop), range(start, stop), and range(start, stop, step) return range[T] directly. Stop is exclusive; omitted start is zero and omitted step is one. A range is a small copyable value: construction, passing, returning, standard-sum wrapping, and repeated iteration allocate nothing. Iterating a stored range is a counted loop, like a direct range(...) source. Collecting its elements into a collection is fallible.

range[T](...) selects any integer type. Start and stop have type T; step is always NonZero[i64], permitting descending unsigned ranges. Without explicit type arguments, a typed bound or range annotation selects T; otherwise it defaults to i64. Bounds must fit T, so an exclusive u8 stop of 256 is rejected.

A step literal such as -2 converts at compile time, and 0 is a compile-time error. A computed step is converted first, which handles zero as an error value:

def evens(stop: i64, stride: i64) -> Result[list[i64], AllocError | RangeError]:
    step = NonZero.try_from(stride)?
    Ok(list(range(0, stop, step))?)

def main() -> Result[(), AllocError | IoError | RangeError]:
    print(evens(9, 2)?)?
    print(list(range(5, 0, -2))?)?
    Ok(())
[0, 2, 4, 6, 8]
[5, 3, 1]

Passing an i64 step is a compile-time error: range step must be `NonZero[i64]`, not `i64`; write a nonzero literal, or convert a value with `NonZero.try_from(step)?` . A range therefore never has a zero step. A length exceeding i64 traps. Bounds and length handle the full u64 domain without intermediate overflow. len, in, not in, and i64 indexing are supported; negative indices count from the end and missing indices trap. Membership is constant-time.

In a directly written comprehension range, a numeric output annotation can guide the loop variable through direct arithmetic in the output expression:

squares: list[u8] = [n * n for n in range(8)]?

This context does not flow backward through arbitrary calls, filters, stored ranges, or explicit range arguments. Use range[u8](...) at those boundaries. Arithmetic retains its fixed-width rules.