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]:
| Receiver | Operation | Behavior |
|---|---|---|
| Any collection | reserve(additional: i64) | Room for at least this many entries beyond current length |
| List | append(value: T) | Append one element |
| Set | add(value: T) | Insert if absent |
| Dictionary | insert(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.