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

Collection construction and copying

Constructors and displays

list[T].new(), dict[K, V].new(), and set[T].new() return Result[collection, AllocError]. with_capacity(capacity: i64) has the same result and reserves at least that many entries, including hash storage. Aliases expose these constructors too. Empty construction allocates its owner; negative counts and impossible layouts return CapacityOverflow; exhaustion returns OutOfMemory. Partial storage is reclaimed on failure. Small enum bitsets need only the owner allocation; valid capacity hints require no additional storage because all possible members already fit in the membership mask.

[elements], {key: value}, {elements}, and typed empty constructors such as list[i64]() also return Result[collection, AllocError]. {} is a dictionary. Entries must have identical types. Empty displays require contextual types, such as the annotation in names: list[str] = []?; a typed constructor such as list[str]()? needs none. Nonempty displays infer from their first entry unless context supplies the type. Already typed entries never implicitly narrow.

List displays reserve space for all entries before evaluating any of them; their element buffer does not grow during insertion. Other displays and comprehensions start with an empty owner and grow as needed. Entries evaluate left to right after initial storage succeeds. A failed insertion stops evaluation and destroys the initialized prefix; unevaluated entries have no effects. Nested fallible expressions require their own ?. A display’s error is always AllocError, so ? on it needs a function returning Result[_, AllocError] or an error sum containing AllocError, such as Result[_, AllocError | IoError] when it also prints. Otherwise match the result and convert the error explicitly. Propagation leaves the enclosing function, not merely the display. Comprehension evaluation follows the iteration rules.

A directly iterated nonempty i64 list display with constant integer entries at a constant step, such as for n in [2, 4, 6]?:, is lowered to a counted loop without collection storage. Repeated entries, descending sequences, singletons, .unwrap(), and reversed(...) are supported too, provided the step fits i64. Borrowed sources, other element types, nonuniform sequences, and expressions with effects retain ordinary materialization. This is an allocation optimization: the source still needs valid Result handling, including a compatible enclosing error type for ?. Use range when a sequence is naturally described by bounds and a step.

Reservation and insertion

These exclusive methods return Result[(), AllocError]:

ReceiverOperationBehavior
Any collectionreserve(additional: i64)Room for at least this many entries beyond current length
Listappend(value: T)Append one element
Setadd(value: T)Insert if absent
Dictionaryinsert(key: K, value: V)Insert or replace without changing existing key order

dictionary.get_or_insert(key: K, default: V) follows the same growth and failure rules but returns Result[&mut V, AllocError]: a present key keeps its entry and drops both arguments; see dictionary lookup.

Growth reserves all required storage before mutation. Failure preserves contents, order, and lookup behavior; capacity may change. Negative or impossible counts return CapacityOverflow. A successful reserve permits the specified number of subsequent insertions without storage allocation. Existing-key replacement and duplicate set insertion do not grow storage. An equal incoming key is destroyed; the existing key owner is retained. This can run custom cleanup even when the collection length does not change.

Insertion consumes its arguments on both outcomes; failure drops owned inputs. Reserve before moving an input that must survive a storage failure. The Result remains fallible even when no growth is needed.

Conversion

list[T].from(source) and set[T].from(source) consume an owned collection, range, or generator and return Result[collection, AllocError]. list(source) and set(source) infer the element type; list[T](source) and set[T](source) specify it explicitly. These call-style constructors also accept str and bytes: list("aé")? produces ["a", "é"], and list(b"AB")? produces [65u8, 66u8]. The .from(...) methods do not accept those two source types. dict(d) transfers a dictionary. Set conversion retains the first occurrence of each permitted member.

Failure destroys partial output, the current element, and the remaining source; generator effects already performed are not rolled back. Borrowed sources and pair-iterable dictionary construction are unsupported.

Explicit copying

copy(value) -> Result[T, AllocError] observes its operand once, including references to T. Mutable contents are recursively duplicated; immutable strings can share storage. Scalars and wholly immutable values need no allocation. Classes with custom cleanup, aggregates containing them, and generators are uncopyable. A nested Shared handle gains another owner, as share() would, and its content is not copied, so content behind a handle may be of any type. copy(handle) of a handle itself is rejected; write handle.share().

A failed copy preserves the source and releases newly initialized contents. Partial records do not run whole-record destructors. Copying follows the ownership rules; it introduces no mutable aliases.