Module collections/array_list
Dynamic array (vector) with amortized O(1) push and O(1) indexed access.
Stability
stable — every breaking flip this type owed Rust shipped in v0.2.28:
push is infallible with try_push beside it (D9), iter() yields
POINTERS while into_iter() yields values (D14), and sort is the stable
merge with sort_unstable as the heapsort (D17). The P1 battery is
complete and test-covered, and plans/archive/STD_API_STABILIZATION.md §4 lists
nothing still open against this module, so further work here is additive.
Types
Generic growable array similar to Rust's Vec<T>.
Heap-allocated with automatic resizing and reference-counted cleanup.
Type Parameters
| Name | Type | Notes |
|---|---|---|
T | Type | comptime |
Trait Implementations
impl(generic(T : Type), ArrayList(T), ...)
len : (fn(self : &Self) -> usize)Number of elements. O(1) — the length is stored, never counted.
Returns: usize
is_empty : (fn(self : &Self) -> bool)True when the list holds no elements — len() == 0.
Returns: bool
capacity : (fn(self : &Self) -> usize)Elements the current buffer can hold before push has to reallocate.
SIZE_MAX for a zero-sized element type, which never grows — see
_zst_anchor. Rust's Vec::capacity reports usize::MAX for the same
reason.
Returns: usize
_cap : (fn(self : &Self) -> usize)Returns: usize
_owned : (fn(self : &Self) -> bool)Returns: bool
_set_cap : (fn(self : &mut Self, cap : usize) -> unit)Returns: unit
_buf_resize : (fn(self : &mut Self, bytes : usize) -> ?*void)Returns: ?*void
_buf_free : (fn(self : &Self, p : &*T) -> unit)Returns: unit
allocator : (fn(self : &Self) -> Option(Allocator))ptr : (fn(self : &Self) -> ?*T)Raw pointer to the element buffer, or .None when nothing is allocated
yet — Rust's Vec::as_ptr, minus the dangling-pointer guarantee.
The pointer is only valid until the next reallocation (push past
capacity, reserve, ensure_total_capacity, shrink_to_fit), and it
borrows: it does not keep the list alive.
Returns: ?*T
spare_capacity : (fn(self : &mut Self) -> Option(*T))A pointer to the first uninitialized slot, ptr() + len(): the start of
the capacity() - len() slots a reader fills before assume_init
commits them. .None when nothing is allocated yet (reserve first).
The result is a raw pointer VALUE, so the safe-mode gate keeps it out of
safe code exactly as it keeps out ptr(). Claiming memory initialized
needs this token, which safe code cannot obtain (ATS's view of the region;
issues/fixed/safe-code-reaches-freed-memory-through-arraylist-set-len.md).
A struct that merely CONTAINS a pointer, such as RawSlice(T), would not
be gated: safe code may hold String.raw_bytes(). The pointer is valid
until the next reallocation, like ptr().
Returns: Option(*T)
assume_init : (
fn(
self : &mut Self,
n : usize,
spare : &*T,
requires(n <= (self.capacity() - self.len())),
ensures(self.len() == (old(self.len()) + n)),
assumed()
) -> unit
)Commit the first n spare slots (spare is the token from
spare_capacity) as live elements: the length grows by n. Nothing is
initialized or dropped; the caller has written those n slots. PANICS
unless spare is this list's current end and n <= capacity() - len().
Returns: unit
new : (fn(ensures(r.len() == usize(0)), assumed()) -> (r : Self))Empty list on the global allocator. Allocates nothing — the first
push buys the buffer (four elements), as Rust's Vec::new does.
new_in(a) places the buffer in a instead.
Returns: (r : Self)
slice_copy : (fn(self : &Self, r : Range(usize)) -> Self)Owned copy of the elements in [r.start, r.end) — JS-style range
slicing with value semantics (plans/archive/SLICE_REWORK.md: list(a..b)
lowers to this).
The end is CLAMPED to the length rather than checked, so an over-long
range yields a short list instead of panicking; slice is the checked,
Result-returning spelling. Elements are copied (dup'd for a
refcounted T), so the result is independent of the source.
Returns: Self
slice_copy_inclusive : (fn(self : &Self, r : RangeInclusive(usize)) -> Self)Inclusive-range companion of slice_copy (list(a..=b)).
Returns: Self
from : (fn(generic(N : usize), sink(arr) : Array(T, N)) -> Self)Consuming constructor from a fixed-size array — Rust's
Vec::from([T; N]): ArrayList(i32).from([i32(1), i32(2), i32(3)]).
The array is taken by sink, so the caller's binding ends at the call
and its elements are now the list's. A caller that keeps the array
passes a copy: ArrayList(T).from(arr.clone()).
N comes from the array's type, so the exact capacity is reserved up
front and no growth happens.
Returns: Self
with_capacity : (fn(cap : usize, ensures(r.len() == usize(0)), assumed()) -> (r : Self))Empty list whose buffer already holds cap elements, on the global
allocator — Rust's Vec::with_capacity.
PANICS on "capacity overflow" when sizeof(T) * cap would wrap (Rust
panics with the same message), and on allocation failure. A zero-sized
element type takes the one-byte anchor buffer instead of allocating
cap of nothing.
Returns: (r : Self)
new_in : (fn(alloc : Allocator) -> Self)Empty list whose buffer lives in alloc — Rust's Vec::new_in.
Every growth, shrink and the final release go through alloc, wherever
and on whichever thread the last reference dies. Unlike new, this takes
one allocation right away: a block holding only alloc's 16-byte owner
prefix, which is how the list remembers its allocator (the list object
itself stays the size of a plain one). PANICS when that allocation fails.
Returns: Self
with_capacity_in : (fn(alloc : Allocator, cap : usize) -> Self)Empty list with room for cap elements, its buffer in alloc — Rust's
Vec::with_capacity_in. PANICS on capacity overflow and on allocation
failure, like with_capacity.
Returns: Self
_zst_anchor : (fn() -> Self)The one buffer an ArrayList of a ZERO-SIZED element type ever has.
sizeof(T) == 0 (unit, an all-unit aggregate, Array(unit, N)) means
the elements occupy no bytes: a store writes nothing, a read reads nothing,
and the byte size of every buffer is 0. Rust's Vec<()> never allocates
and reports capacity() == usize::MAX. Yo cannot conjure a non-null
dangling pointer, so the stand-in is ONE one-byte block, allocated once and
never resized: the capacity is the largest untagged value (capacity()
reports SIZE_MAX), so push never reaches the grow
path, and shrink_to_fit is a no-op. Going through the allocator with
sizeof(T) * cap == 0 instead would malloc(0) and then realloc(p, 0) —
which frees p and returns NULL on glibc and the Windows CRT — and the
list would report "allocation failed" on its second growth.
Returns: Self
_zst_anchor_buf : (fn() -> *T)Returns: *T
try_push : (fn(self : &mut Self, value : T) -> Result(unit, ArrayListError))Append an element, reporting allocation failure as
.Err(.AllocError(.OutOfMemory)) instead of panicking.
The allocator-aware form (D9); push is the one to reach for. Amortized
O(1) — the buffer doubles (4 elements first), so a growth INVALIDATES
every pointer previously handed out by ptr(), iter() or the Index
impl.
Returns: Result(unit, ArrayListError)
push : (
fn(
self : &mut Self,
value : T,
// The element clause is proof-only (it quantifies), so it is never
// spliced as a runtime assert; the verifier uses it to prove what a
// loop of pushes built.
ensures(
self.len() == (old(self.len()) + usize(1)),
forall(k : usize, (k < self.len()) ==> (self(k) == cond((k == old(self.len())) => value, true => old(self)(k))))
),
assumed()
) -> unit
)Append an element — amortized O(1).
PANICS on allocation failure: Rust's Vec::push aborts, and so do this
list's own ensure_total_capacity / with_capacity. Before D9 this
returned a Result that every one of its callers discarded, usually via
a throwaway _p := binding — a Result nobody checks is strictly worse
than a panic, because it reads as if the failure were handled. Use
try_push when the caller genuinely handles OOM.
A growth reallocates, so any pointer into the buffer (ptr(), iter(),
self(i)) is invalid afterwards.
Returns: unit
pop : (fn(self : &mut Self) -> Option(T))Remove and return the last element, or .None when empty — O(1), and
the capacity is kept.
Returns: Option(T)
get : (fn(self : &Self, index : usize) -> Option(T))Copy of the element at index, or .None when index >= len().
The bounds-checked reader. self(index) (the Index impl) reads the
element in place and panics out of range instead; get_cloned(i)
clones an element that is not copied implicitly, and with(i, body)
writes one in place (plans/VALUES_BY_DEFAULT.md decision 20).
Returns: Option(T)
shrink_to_fit : (fn(self : &mut Self) -> Result(unit, ArrayListError))Give the unused tail of the buffer back to the allocator so that
capacity() == len() — Rust's Vec::shrink_to_fit.
Reallocates (an empty list frees its buffer outright), so every pointer
into the buffer is invalid afterwards and the next push grows again.
.Err(.AllocError(.OutOfMemory)) when the shrinking realloc fails —
the list is left untouched and usable. A zero-sized element type is a
no-op: its anchor buffer cannot shrink.
Returns: Result(unit, ArrayListError)
_free_elements : (fn(self : &mut Self) -> unit)Returns: unit
drain : (
fn(
self : &mut Self,
r : Range(usize),
requires(r.start <= r.end, r.end <= self.len()),
ensures(self.len() == (old(self.len()) - (r.end - r.start))),
assumed()
) -> Self
)Remove the elements in [r.start, r.end) and return them, in order, as
a fresh list — Rust's Vec::drain, collected eagerly rather than lazily
(Yo has no borrowing iterator to hold the source open).
O(n): the tail shifts left to close the gap. PANICS on an inverted range
or an end past the length — the old remove(start, count) silently
clamped the count, which hid caller bugs.
Returns: Self
insert : (
fn(
self : &mut Self,
idx : usize,
value : T,
requires(idx <= self.len()),
ensures(self.len() == (old(self.len()) + usize(1))),
assumed()
) -> unit
)Insert value at idx, shifting the tail right — O(n). idx == len
appends, as in Rust.
Returns: unit
append : (fn(self : &mut Self, other : &mut Self) -> unit)Move every element of other onto the end of self, leaving other
EMPTY — Rust's append. extend copies instead.
Returns: unit
first : (fn(self : &Self) -> Option(T))First element, or .None when empty.
Returns: Option(T)
last : (fn(self : &Self) -> Option(T))Last element, or .None when empty.
Returns: Option(T)
reserve : (fn(self : &mut Self, additional : usize) -> unit)Reserve room for additional MORE elements.
Rust's spelling. ensure_total_capacity takes a TOTAL, which is the same
call with a different meaning and an easy one to get wrong at a call site.
Returns: unit
with : (fn(generic(R : Type), self : &mut Self, index : usize, body : &Impl(Fn(v : &mut T) -> R)) -> R)Run body on the element at index, borrowed in place, and return
its result — decision 20's non-copying access (plans/VALUES_BY_DEFAULT.md):
the element is neither copied out nor written back. Panics when
index >= len().
{ ArrayList } :: import("std/collections/array_list");
{ String } :: import("std/string");
(mut(names) : ArrayList(String)) = ArrayList(String).new();
names.push(`ada`);
n := names.with(usize(0), s => {
s.push_str(" lovelace");
s.len()
});
assert((n == usize(12)) && (names(usize(0)) == `ada lovelace`), "written in place");
Returns: R
take : (fn(self : &mut Self, index : usize, where(T <: Default)) -> T)Move the element at index out and leave T's default in its slot —
std/mem's take(&mut xs(i)). Nothing is copied, so a move-only T
works. Panics when index >= len().
{ ArrayList } :: import("std/collections/array_list");
{ String } :: import("std/string");
(mut(names) : ArrayList(String)) = ArrayList(String).new();
names.push(`ada`);
s := names.take(usize(0));
assert((s == `ada`) && (names(usize(0)) == ``), "the slot keeps the default");
Returns: T
swap : (
fn(
self : &mut Self,
i : usize,
j : usize,
requires(i < self.len(), j < self.len()),
ensures(self.len() == old(self.len())),
assumed()
) -> unit
)Exchange the elements at i and j.
Moves the raw slots, like reverse — no dup/drop, so this is RC-neutral
however the element type is refcounted.
Returns: unit
swap_remove : (
fn(
self : &mut Self,
idx : usize,
requires(idx < self.len()),
ensures(self.len() == (old(self.len()) - usize(1))),
assumed()
) -> (r : T)
)Remove the element at idx and return it, moving the LAST element into
the hole — O(1), and it does not preserve order. Rust's swap_remove.
Returns: (r : T)
truncate : (fn(self : &mut Self, new_len : usize) -> unit)Shorten to new_len, dropping the elements past it. Longer new_len is
a no-op, as in Rust.
Returns: unit
resize : (fn(self : &mut Self, new_len : usize, value : &T) -> unit)Resize to exactly new_len, dropping the tail when shrinking and
appending copies of value when growing. Rust's resize.
Distinct from resize_with_byte, which memsets raw storage and is only
meaningful for a plain-old-data element type.
Returns: unit
fill : (fn(self : &mut Self, value : &T) -> unit)Overwrite every element with value, keeping the length. Rust's fill.
Returns: unit
split_off : (fn(self : &mut Self, at : usize) -> Self)Split off [at, len) into a new list, leaving [0, at) here.
Returns: Self
remove : (
fn(
self : &mut Self,
idx : usize,
requires(idx < self.len()),
ensures(self.len() == (old(self.len()) - usize(1))),
assumed()
) -> (r : T)
)Remove the element at idx and return it, shifting the tail left —
O(n), order preserved. Rust's Vec::remove.
PANICS when idx >= len(). swap_remove is the O(1) alternative when
order does not matter.
Returns: (r : T)
slice : (fn(self : &Self, start : usize, end : usize) -> Result(Self, ArrayListError))Owned copy of [start, end), or an .IndexOutOfBounds error — the
CHECKED counterpart of slice_copy, which clamps instead.
An inverted range reports .IndexOutOfBounds(index : start, length : end). O(end - start), one allocation of the exact size.
Returns: Result(Self, ArrayListError)
ensure_total_capacity : (fn(self : &mut Self, min_cap : usize) -> unit)Grow the buffer so it holds at least min_cap elements in TOTAL,
doubling as it goes; a request under the current capacity does nothing.
reserve(additional) is the Rust spelling and takes a COUNT TO ADD —
the same call with a different meaning, and the easy one to get right at
a call site. PANICS on capacity overflow and on allocation failure
(try_push is the only allocator-aware entry point). Reallocates, so
pointers into the buffer do not survive it.
Returns: unit
extend_from_ptr : (fn(self : &mut Self, src : &*T, count : usize) -> unit)Append count elements from a raw buffer with one memcpy.
The caller guarantees src holds count initialized, correctly aligned
Ts that do not alias this list's buffer. The bytes are copied WITHOUT
per-element dup, so for a refcounted T this MOVES the source elements:
the caller must not also release them. Reaching for it on a plain-data
element type is the point — that is where it beats a push loop.
Returns: unit
clear : (fn(self : &mut Self) -> unit)Drop every element and set the length to 0, KEEPING the capacity —
Rust's Vec::clear. Use shrink_to_fit afterwards to release the
buffer too.
Returns: unit
fill_with_byte : (fn(self : &mut Self, byte_val : int) -> unit)Overwrite the live len() elements with a repeated byte via memset —
the fast way to zero a bool / integer list.
Writes over the elements without dropping them, so it is only sound for
a plain-data T: on a refcounted element type it strands every handle
it overwrites. fill(value) is the type-safe form.
Returns: unit
resize_with_byte : (fn(self : &mut Self, new_len : usize, byte_val : int) -> unit)Set the length to exactly new_len, memsetting any newly exposed
slots to a repeated byte.
Shrinking does NOT run destructors and growing does not construct
anything, so — like fill_with_byte — this is for a plain-data T
only; resize(new_len, value) is the type-safe form. Growing may
reallocate and can panic on allocation failure.
Returns: unit
impl(generic(T : Type), ArrayList(T), Index(usize)(...))
Output : TThe output type is the element type T.
index : (fn(self : &Self, idx : usize) -> &Self.Output)The element at idx, read (a read projection). Panics if the index
is out of bounds.
Parameters
| Name | Type | Notes |
|---|---|---|
idx | usize |
Returns: &Self.Output
impl(generic(T : Type), ArrayList(T), IndexMut(usize)(...))
Output : TThe output type is the element type T.
index_mut : (fn(self : &mut Self, idx : usize) -> &mut Self.Output)The element at idx, written (a mutable projection). Panics if the
index is out of bounds.
Parameters
| Name | Type | Notes |
|---|---|---|
idx | usize |
Returns: &mut Self.Output
impl(generic(T : Type), ArrayList(T), IndexUnchecked(...))
Output : TThe output type is the element type T, the same as Index(usize)'s.
index_unchecked : (fn(self : &Self, idx : usize) -> *Self.Output)index minus the length test. Codegen calls this only at a subscript
the verifier proved in bounds
(plans/backlog/SAFE_MODE_5B_CONTAINER_BOUNDS_ELISION.md).
Parameters
| Name | Type | Notes |
|---|---|---|
idx | usize |
Returns: *Self.Output
impl(generic(T : Type), ArrayList(T), IndexUncheckedMut(...))
Output : TThe output type is the element type T, the same as IndexMut(usize)'s.
index_unchecked_mut : (fn(self : &mut Self, idx : usize) -> *Self.Output)index_mut minus the length test, on the same terms as
index_unchecked.
Parameters
| Name | Type | Notes |
|---|---|---|
idx | usize |
Returns: *Self.Output
impl(generic(T : Type), ArrayList(T), Drop(...))
drop : (fn(self : &mut Self) -> unit)Drops every live element and frees the buffer. Runs when the last reference to the list goes away, not at the end of the binding's scope.
Returns: unit
impl(generic(T : Type), ArrayList(T), Trace(...))
trace : (fn(self : &Self, tracer : GcTracer) -> unit)Cycle-GC tracing. The elements live in a malloc'd buffer the
compiler's auto-derived field walk cannot reach, so trace each
element's buffer slot. tracer.visit takes the slot POINTER and reads
it WITHOUT touching the element's reference count — a by-value managed
handle would be dup'd then dropped, freeing a live element
mid-collection.
Parameters
| Name | Type | Notes |
|---|---|---|
tracer | GcTracer |
Returns: unit
impl(generic(T : Type), ArrayList(T), IntoIterator(...))
Item : TIntoIter : ArrayListIter(T)into_iter : (fn(self : Self) -> ArrayListIter(T))Returns: ArrayListIter(T)
impl(generic(T : Type), ArrayList(T), ...)
iter : (fn(self : &Self) -> ArrayListIterPtr(T))Raw walk over the buffer: yields a *(T) into the list's buffer and
leaves the list intact — for(list.iter(), ptr => ...), where
into_iter consumes the list and yields values (D14; the iter /
into_iter split every other std collection already had). Safe code
reads elements with for(&list, x => ...); this is the
pragma(Pragma.AllowUnsafe) form (decision 39).
The iterator holds the buffer pointer and the current length, not the list (B7b), so the list must outlive the walk, and must not grow or shrink during it: a reallocation invalidates the pointers already yielded.
Returns: ArrayListIterPtr(T)
indices : (fn(self : &Self) -> Range(usize))The positions 0 .. len(), a Range(usize) that holds no handle to the
list: the index cursor of decision 39 (plans/VALUES_BY_DEFAULT.md).
Read each element through the list, xs(i), so every step re-derives
it from the current buffer: a push during the walk is seen at the next
step, and an index past a shrink is the out-of-bounds panic, never a read
of a freed buffer. The range is fixed when it is made.
Returns: Range(usize)
impl(generic(T : Type), ArrayList(T), BorrowedWalk(...))
WalkCursor : usizeWalkElem : Twalk_start : (fn(self : &Self) -> usize)The first position (equal to the end for an empty collection).
Returns: usize
walk_valid : (fn(self : &Self, cur : &usize) -> bool)Whether cur names an element.
Parameters
| Name | Type | Notes |
|---|---|---|
cur | &usize |
Returns: bool
walk_ref : (fn(self : &Self, cur : &usize) -> &T)The element at cur, read in place (a read projection). Panics if cur
names no element.
Parameters
| Name | Type | Notes |
|---|---|---|
cur | &usize |
Returns: &T
walk_next : (fn(self : &Self, cur : &usize) -> usize)The position after cur.
Parameters
| Name | Type | Notes |
|---|---|---|
cur | &usize |
Returns: usize
impl(generic(T : Type), ArrayList(T), BorrowedWalkMut(...))
walk_elem : (fn(self : &mut Self, cur : &usize) -> &mut T)The element at cur, written in place (a mutable projection). Panics
if cur names no element.
Parameters
| Name | Type | Notes |
|---|---|---|
cur | &usize |
Returns: &mut T
impl(generic(T : Type), where(T <: Eq(T)), ArrayList(T), ...)
dedup : (fn(self : &mut Self) -> unit)Remove CONSECUTIVE duplicates, keeping the first of each run — Rust's
dedup. Only adjacent equals collapse, so [1,2,1] is unchanged; sort
first if you want set semantics.
Returns: unit
starts_with : (fn(self : &Self, prefix : &Self) -> bool)Whether self begins with prefix — Rust's starts_with.
Returns: bool
ends_with : (fn(self : &Self, suffix : &Self) -> bool)Whether self ends with suffix — Rust's ends_with.
Returns: bool
contains : (fn(self : &Self, value : &T) -> bool)Whether any element equals value — a linear scan, O(n), stopping at
the first match. Rust's Vec::contains.
Returns: bool
index_of : (fn(self : &Self, value : &T) -> Option(usize))Index of the FIRST element equal to value, or .None — a linear
scan, O(n). Rust spells this iter().position(...); on a sorted list
reach for binary_search instead.
Returns: Option(usize)
impl(generic(T : Type), ArrayList(T), ...)
reverse : (fn(self : &mut Self) -> unit)Reverse the elements in place, O(n).
Exchanges raw slots bitwise, like swap, so no element is copied or
dropped: RC-neutral however T is refcounted, and a move-only T
reverses too.
Returns: unit
impl(generic(T : Type), where(T <: Ord(T)), ArrayList(T), ...)
sort : (fn(self : &mut Self) -> unit)Sort ascending by Ord. STABLE (D17): equal elements keep their input
order, as sort does in Rust, Python, Java and Go.
O(n log n) with one usize index array plus scratch allocated for the
merge — sort_unstable is the allocation-free heapsort for when order
among equals does not matter.
Returns: unit
sort_unstable : (fn(self : &mut Self) -> unit)Sort ascending by Ord WITHOUT preserving the order of equal elements
— Rust's sort_unstable. Heapsort: O(n log n) worst case, in place, no
allocation, no recursion (see _heapsort_by).
Returns: unit
impl(generic(T : Type), where(T <: Eq(T)), ArrayList(T), Eq(ArrayList(T))(...))
impl(generic(T : Type), ArrayList(T), Default(...))
default : (fn() -> Self)The default value of the type.
Returns: Self
impl(generic(T : Type), where(T <: Clone), ArrayList(T), Clone(...))
clone : (fn(self : &Self) -> Self)Create an independent clone of self.
Returns: Self
impl(generic(T : Type), ArrayList(T), FromIterator(...))
Elem : Tfrom_iter_new : (fn() -> Self)The empty collection collect starts from.
Returns: Self
from_iter_add : (fn(sink(acc) : Self, item : &T) -> Self)Add one element to a partially-built collection and return it.
Parameters
| Name | Type | Notes |
|---|---|---|
item | &T |
Returns: Self
impl(generic(T : Type), ArrayList(T), ...)
sort_by : (fn(self : &mut Self, less : &Impl(Fn(a : &T, b : &T) -> bool)) -> unit)Sort by a caller-supplied strict "less" — less(a, b) is true when a
must come before b. STABLE: equal elements keep their input order,
matching Rust's sort_by. Use sort_unstable_by for the
allocation-free heapsort.
Returns: unit
sort_by_key : (
fn(
generic(K : Type),
self : &mut Self,
key : &Impl(Fn(v : &T) -> K),
where(K <: Ord(K))
) -> unit
)Sort ascending by a KEY extracted from each element — Rust's
sort_by_key. Stable, like sort_by, which it delegates to.
The key type is a method-level generic(K : Type), which the clause
order requires to come BEFORE self; it is inferred from the closure at
the call site, so xs.sort_by_key(p => p.age) needs no annotation.
Returns: unit
binary_search_by : (fn(self : &Self, cmp : &Impl(Fn(v : &T) -> Ordering)) -> Result(usize, usize))Binary search over a list ordered by cmp — Rust's binary_search_by.
cmp reports how each ELEMENT orders against the target (.Less when
the element comes first), so searching does not require constructing a
T to compare against — which is the whole reason to reach for this
over binary_search. .Ok(index) of a match (any one of an equal run),
or .Err(insertion_index).
Returns: Result(usize, usize)
chunks : (fn(self : Self, size : usize) -> ArrayListChunks(T))Non-overlapping chunks of size, in order — Rust's chunks. The final
chunk is SHORT when the length is not a multiple of size.
PANICS on size == 0, as Rust does.
Consumes the list (D20).
Returns: ArrayListChunks(T)
windows : (fn(self : Self, size : usize) -> ArrayListWindows(T))Overlapping windows of EXACTLY size, advancing one element at a time —
Rust's windows. Yields nothing when the list is shorter than size.
PANICS on size == 0, as Rust does.
Consumes the list (D20).
Returns: ArrayListWindows(T)
sort_unstable_by : (fn(self : &mut Self, less : &Impl(Fn(a : &T, b : &T) -> bool)) -> unit)Sort by a caller-supplied strict "less" WITHOUT preserving the order of
equal elements (heapsort). Rust's sort_unstable_by.
Returns: unit
retain : (fn(self : &mut Self, pred : &Impl(Fn(v : &T) -> bool)) -> unit)Keep only the elements pred accepts, in place and in order; the
rejected elements are dropped. One pass: the survivors are collected
into a fresh list, clear releases every original, extend moves the
survivors back — O(n) with a single allocation, and every RC step is one
of the module's proven public operations (push / clear / extend). The
first cut compacted through raw pointers and double-released RC elements
(the Linux-ASan heap-use-after-free on PR #313's first run; local ASan is
non-functional, so CI was the first to see it); the second walked
backwards over drain, which was O(n²) with an allocation per rejection
(issues/fixed/array-list-retain-is-quadratic.md).
Returns: unit
extend : (fn(self : &mut Self, other : ArrayList(T)) -> unit)Append every element of other, consuming it — Rust's
Vec::extend(other). extend_cloned(&other) appends clones and keeps
other.
When other is the only handle to its list, the elements MOVE: one
memcpy, and the emptied list is freed. While ArrayList is still a
shared handle (until plans/VALUES_BY_DEFAULT.md's B8), another handle
may share the list; the elements are then copied, so the other handle
keeps them (lookahead D21's rule for the consuming iterators). That
copy is decision 20's check, so a move-only T is E0901 here until B8.
Returns: unit
impl(generic(T : Type), where(T <: Clone), ArrayList(T), ...)
extend_cloned : (fn(self : &mut Self, other : &ArrayList(T)) -> unit)Append a clone of every element of other, which is left as it was.
Each element is cloned in place through other(i).
Returns: unit
get_cloned : (fn(self : &Self, index : usize) -> Option(T))A clone of the element at index, or .None when index >= len() —
get for an element that is not copied implicitly (decision 20). The
element is cloned in place, never copied out first.
{ ArrayList } :: import("std/collections/array_list");
{ String } :: import("std/string");
(mut(names) : ArrayList(String)) = ArrayList(String).new();
names.push(`ada`);
assert(names.get_cloned(usize(0)) == .Some(`ada`), "a clone of the element");
Returns: Option(T)
first_cloned : (fn(self : &Self) -> Option(T))A clone of the first element, or .None when empty.
Returns: Option(T)
last_cloned : (fn(self : &Self) -> Option(T))A clone of the last element, or .None when empty.
Returns: Option(T)
impl(generic(T : Type), where(T <: Ord(T)), ArrayList(T), ...)
binary_search : (fn(self : &Self, value : &T) -> Result(usize, usize))Binary search over a SORTED list (ascending, sort's order):
.Ok(index) of a matching element (any one of an equal run), or
.Err(insertion_index) where the value would keep the order.
Returns: Result(usize, usize)
Error variants for ArrayList operations.
Variants
| Variant | Fields | Description |
|---|---|---|
AllocError | error: AllocError | Memory allocation failed. |
IndexOutOfBounds | index: usize, length: usize | Index is out of bounds for the current length. |
EmptyList | Attempted to access an element from an empty list. |
Methods
clone : (ArrayListError) fn(self : &ArrayListError) -> ArrayListErrorValue iterator over an ArrayList — yields each element BY VALUE, which
dups a refcounted T. This is what into_iter() (and therefore
for(list, ...)) returns; iter() yields pointers instead (D14).
It OWNS the list: into_iter takes it by value (B7b). Until ArrayList
is a value type that is a shared handle, so the elements are still copied
out rather than moved (lookahead D21), and _length is re-read on every
next: a push through another handle during a forward walk extends the
walk. Double-ended: next_back takes from the far end.
Type Parameters
| Name | Type | Notes |
|---|---|---|
T | Type | comptime |
Trait Implementations
impl(generic(T : Type), ArrayListIter(T), Iterator(...))
Item : Tnext : (fn(self : &mut Self) -> Option(T))Advance the iterator and return the next value, or None when exhausted.
Returns: Option(T)
impl(generic(T : Type), ArrayListIter(T), DoubleEndedIterator(...))
Item : Tnext_back : (fn(self : &mut Self) -> Option(T))Advance the iterator from the back and return the previous value, or
None when the two ends have met.
Returns: Option(T)
Pointer iterator over an ArrayList — yields *(T) INTO the list's own
buffer (D14). This is what iter() returns; into_iter() keeps yielding
values.
The elements are borrowed, not dup'd, so iterating a list of RC values
costs no refcount traffic and writing through the pointer mutates the list
in place. HashMapIterPtr established the shape.
A raw view (decision 39, B7b): it holds the buffer pointer and the length
iter() saw, NOT the list, so it keeps nothing alive and is meant for
pragma(Pragma.AllowUnsafe) code. The list must outlive the walk and keep
its length: any operation that REALLOCATES the buffer (push past
capacity, reserve, shrink_to_fit) invalidates every pointer, and a
shrink leaves the walk reading past the live elements.
Type Parameters
| Name | Type | Notes |
|---|---|---|
T | Type | comptime |
Trait Implementations
impl(generic(T : Type), ArrayListIterPtr(T), Iterator(...))
Item : *Tnext : (fn(self : &mut Self) -> Option(*T))Advance the iterator and return the next value, or None when exhausted.
Returns: Option(*T)
impl(generic(T : Type), ArrayListIterPtr(T), DoubleEndedIterator(...))
Item : *Tnext_back : (fn(self : &mut Self) -> Option(*T))Advance the iterator from the back and return the previous value, or
None when the two ends have met.
Returns: Option(*T)
Iterator over non-overlapping CHUNKS of a list.
Yo has no slice type, so each chunk is a freshly allocated ArrayList(T)
COPY rather than a borrowed view — the honest rendering of Rust's chunks,
whose item is a &[T]. Mutating a yielded chunk does not touch the source.
The iterator owns its source: chunks takes the list by value (lookahead
D20), so xs.clone().chunks(n) walks a list that is read again later.
Type Parameters
| Name | Type | Notes |
|---|---|---|
T | Type | comptime |
Trait Implementations
Iterator over overlapping WINDOWS of a list — see ArrayListChunks for why
each window is a copy.
Type Parameters
| Name | Type | Notes |
|---|---|---|
T | Type | comptime |