std.iter
std.iter defines the two small cursor interfaces used by synchronous for loops, plus the current enumeration, adapter, and terminal implementations. Sources are not represented by a public interface: a concrete source exposes the applicable structural iter receiver overload, while generic algorithms accept an explicit cursor.
Contract
An Iterator<T> is the owner-yield cursor interface. ExclusiveIterator<T> is the distinct complete-element lending interface:
interface Iterator<T> {
fn next(&self) -> ^T?;
}
interface ExclusiveIterator<T> {
fn next<region Step>(&self in Step) -> &T? in Step;
}A successful Iterator<T>.next returns a fresh ^T owner with no Step loan. A successful ExclusiveIterator<T>.next instead creates one fresh call-local Step; its &T loan must end before the next pull. Both interfaces are infallible, but their ownership results are deliberately distinct.
Both are cursor interfaces, not source interfaces. Every accepted stateful std cursor is affine and one-shot: it does not implement Copy or Clone, and its iteration surface is next, not another iter method. The accepted target has no Iterable<T, Iter>, IntoIterator, Sequence, Range, or cursor-copy interface. The checked-in std.iter.iterable module and conformances remain transitional until this migration is implemented.
Exhaustion is sticky for every accepted synchronous cursor shape. Once a next call returns null normally, every later next returns null without advancing or inspecting an underlying source, invoking user code, allocating or deallocating, performing I/O or entropy work, mutating observable state, or reporting a checked error. A lending null carries no Step loan. A checked failure is not silently converted into exhaustion; recovery after a caught failure follows the concrete fallible cursor's own contract.
ExclusiveIterator<T> remains the interface for a collection that may lend its complete element as &T. A collection that permits mutation of selected projections instead uses an ordinary concrete structural cursor returning an affine region-typed view:
struct View<region Step> {
let read: T in Step,
let write: &U in Step,
}
fn next<region Step>(&self in Step) -> View? in Step;This is a general language shape, not a new interface or a recognized collection/view name. An eligible view contains only shared or exclusive loans plus copy metadata; it has no owned field and no destructor. Stored roles determine exactly what the caller may read or mutate. A present view must end before the next next; null carries no loan. There is no associated-type or higher-kinded ViewIterator interface.
The source expression is evaluated once. If its type has an accepted synchronous cursor-shaped next, the loop drives that exact cursor directly. Direct next classification wins even when the type also has an iter method; an effect or binding mismatch is diagnosed and never falls back to iter. An unrelated method merely named next but matching no accepted cursor shape does not block structural iter resolution. A named cursor is exclusively borrowed for the complete loop, not moved or copied. Consequently break releases the loop loan and leaves the cursor at its exact current position:
let cursor = values.iter();
for value in cursor {
inspect(value);
break;
}
// Continues after the value observed above.
for value in cursor {
inspect(value);
}The cursor name is inaccessible while the loop's exclusive loan is live. A fresh cursor temporary is instead owned by the loop and drops at loop exit. An explicit <-cursor likewise moves a named cursor into the loop; direct next still wins, and an early exit then drops the moved cursor and its remainder.
Only when the source type has no direct synchronous next shape does for resolve a concrete structural iter method. Written receiver access selects exactly one role: source selects iter(self), &source selects iter(&self), and <-source selects iter(^self). Acquisition occurs once, and the returned temporary cursor is then driven through next. There is no public source conformance, source-to-cursor conversion interface, shared fallback, implicit copy, or implicit clone.
An ordinary synchronous cursor whose next() returns T? throws(E) is driven with for try value in cursor; the loop propagates E before body entry. This does not add a fallible std interface: Iterator<T> and ExclusiveIterator<T> remain infallible. A structurally fallible owning, shared, or lending cursor uses the same for try header. A fallible source expression or structural iter acquisition needs its own written try, separate from the loop header.
Receiver access selects collection iteration. for value in source uses only shared/copy iter(self). for value in <-source uses only a consuming iter(^self) overload and moves a named source; no overload means a hard error, not a shared fallback or clone. A fresh owned temporary needs no <-.
The receiver rule applies only after direct-cursor classification. If source itself has next, the loop never calls source.iter().
Mutable lending iteration has one spelling:
for item: &T in _ in &items {
item.field = value;
}The source-side &items selects only a concrete iter<region Source>(&self in Source) -> ^ConcreteExclusiveIter<T> in Source overload. The typed binding is the required explicit local loan and never selects the overload itself. There is no iter_mut alias. The source loan lasts for the complete loop, while each Step item loan ends before the next backedge. The collection name is inaccessible during the loop: no read, second loan, or structural mutation is allowed. Mutating or replacing the current item is allowed; replacing it drops the old value, and <-item is rejected.
Projection-lending iteration uses the same explicit source access and a region-typed affine item instead of pretending the complete element is mutable:
for view: View in _ in &items {
inspect(view.read);
view.write.field = value;
}&items alone selects iter(&self). View in _ records the fresh local step region and never participates in overload selection. The view cannot escape, be consumed, or survive the backedge. HashMapExclusiveEntry uses this rule to keep its key shared and its value exclusive; no mutable key loan is created.
The concrete iterator returned by iter(^self) owns its storage and remaining armed elements. OwnedIter<T> is only this common semantic model, not a new standard interface or compiler type. Each collection may return its own iterator implementing the existing next() -> ^T? contract. There is no consume-all std.slice.drain; the loop or an explicitly obtained consuming iterator is the one path.
std.iter.Iter<T>(items) borrows a [T] slice and copies each yielded element, so T must be Copy. The accepted std.iter.enumerate(...) consumes an explicit owning cursor and yields owning ^(usize, ^T) pairs with a zero-based counter.
Iterators are ordinary affine values that carry their own cursor. They do not own the source slice or collection unless the concrete iterator type says so. The borrowed source must remain alive and must not be mutated in a way that invalidates the iterator while iteration is in progress. The iterator types that store such a view carry its inferred region, so they cannot escape the source lifetime. A binding that stores a borrowed cursor or yielded view writes its inferred region explicitly as in _; region arguments are never put in generic angle brackets. The iterator constructors, enumeration helpers, and lazy adapters do not allocate and do not throw in the checked-in implementation. The accepted target has no materializing iterator terminal; allocation is written at each explicit push while building an owning slice.
Generic code abstracts over an explicit cursor, using Iterator<T> for owned items or ExclusiveIterator<T> for complete-element loans. It does not accept an abstract source and does not call iter through a bound. A caller that has a source chooses its receiver role and passes source.iter(). A generic operation that needs two traversals receives two cursors explicitly. Concrete mixed-view and fallible cursors remain structural rather than forcing associated region-typed items or checked effects into either infallible interface.
str.split and str.lines are concrete borrowed-view cursors in this last category. Their opaque affine SplitCursor<region Life> and LinesCursor<region Source> values expose only infallible next, returning str? in Life or str? in Source. They do not implement the owning-item Iterator<T> interface, and therefore do not acquire collect, count, map, filter, or any other generic owning-cursor terminal or adapter. Construction and stepping allocate nothing. A caller that needs an owning ^[^str] writes the loop, clones each lent fragment, and pushes each owner explicitly; join(split(...)) is not an implicit materialization path. See std.strings for exact delimiter and line semantics.
import std.iter;
fn add(acc: i32, value: i32) -> i32 {
return acc + value;
}
fn main() {
let values: ^[i32] = try ^[1, 2, 3, 4];
let bounded = iter.take(iter.skip(values.iter(), 1), 2);
var total: i32 = 0;
for item in bounded {
total = add(total, item);
}
assert total == 5;
let cursor = iter.Iter<i32>(values);
assert iter.count(<-cursor) == 4 as usize;
for (index, value) in iter.enumerate(values.iter()) {
if index == 1 as usize {
assert value == 2;
}
}
}Counting An Owning Cursor
The accepted target exports exactly one counting terminal:
fn count<T, I: Iterator<T>>(cursor: ^I) -> usize;count consumes an owning cursor. A named cursor therefore requires an explicit move and becomes unavailable after the call:
import std.iter;
fn main() {
let values: ^[i32] = try ^[3, 1, 4, 1, 5];
let cursor = iter.Iter<i32>(values);
let length = iter.count(<-cursor);
assert length == 5 as usize;
// `cursor` was moved.
}The terminal repeatedly calls next, ordinary-drops every yielded owner, and stops after exactly one pull returns sticky null. The increment uses ordinary usize arithmetic. An implementation must not replace these pulls with a size hint or another shortcut that would omit next effects or item destructor effects.
There is no .count() method and no slice or generic-source overload. A slice already knows its element count, so use slice.length. Fallible cursors and lending cursors are outside this infallible owner-consuming signature; count them with an explicit loop so checked failure and step-loan boundaries remain visible.
Folding And Reducing An Owning Cursor
The accepted target has exactly two aggregation terminals:
export fn fold<
T,
A,
I: Iterator<T>,
F: (&self, ^A, ^T) -> ^A,
>(cursor: ^I, initial: ^A, folder: &F) -> ^A;
export fn reduce<
T,
I: Iterator<T>,
F: (&self, ^T, ^T) -> ^T,
>(cursor: ^I, reducer: &F) -> ^T?;Both consume one explicitly selected owner-yield cursor and exclusively borrow one concrete callback for the duration of the call. They do not erase, box, allocate, store, or consume that callback. A named cursor and affine initial value move with <-; a named callback is loaned with & and remains available after normal return:
import std.iter;
fn main() {
let values: ^[i32] = try ^[1, 2, 3, 4];
let cursor = values.iter();
let initial: i32 = 10;
let add = [] (total: ^i32, value: ^i32) -> ^i32 {
return total + value;
};
let total = iter.fold(<-cursor, initial, &add);
assert total == 20;
}fold transfers the exact current accumulator and each exact item owner into the callback. For N items it performs N successful pulls, N callbacks, and one terminal pull. Empty input invokes no callback and returns the exact initial owner.
reduce uses the first yielded owner as its accumulator. Empty input returns null; a singleton returns that exact owner without invoking the callback; N > 0 items produce N - 1 strict-left callback calls and one final terminal pull. Neither name grants associativity or commutativity: optimizers preserve written pull, callback, owner cleanup, trap, and terminal order.
There is no method or slice/source overload. std.iter.sum, min, and max are removed: callers express identity and arithmetic policy with fold, or ordering, equality-tie, NaN, and losing-owner policy with reduce. Fallible, lending, short-circuit, recovery, right, and parallel folds remain explicit loops until separately designed.
Searching A Borrowed Cursor
The accepted target has exactly three predicate short-circuit operations:
export fn any<T, I: Iterator<T>, P: (&self, T) -> bool>(
cursor: &I,
predicate: &P,
) -> bool;
export fn all<T, I: Iterator<T>, P: (&self, T) -> bool>(
cursor: &I,
predicate: &P,
) -> bool;
export fn find<T, I: Iterator<T>, P: (&self, T) -> bool>(
cursor: &I,
predicate: &P,
) -> ^T?;Unlike exhaustive count, fold, and reduce, these functions exclusively borrow the cursor. A short-circuit result preserves its exact remainder:
import std.iter;
fn main() {
let values: ^[i32] = try ^[1, 3, 4, 6];
let cursor = values.iter();
let even = [] (value: i32) -> bool {
return value % 2 == 0;
};
assert iter.any(&cursor, &even);
assert cursor.next() == 6;
}Each predicate receives a shared call-scoped loan of the exact pulled item. Rejected items are destroyed before another pull. any and all also destroy their deciding item before returning; find moves that exact owner into its nullable result. An early decision performs no terminal pull. An exhaustive finite search performs exactly one terminal pull and leaves the named cursor initialized but sticky-exhausted.
The cursor and concrete predicate owners remain with the caller. There is no method, consuming overload, source/slice adaptation, answer-plus-remainder wrapper, implicit callback erasure, or fallible/lending/async family. position, nth, last, and next_or are removed: use an explicit counter loop, skip plus next, keep-right reduce, or a direct null branch.
Bounded Cursor Adapters
The accepted target exports exactly two bounded, lazy cursor adapters:
export struct TakeCursor<T, I: Iterator<T>, region Source>: Iterator<T>;
export struct SkipCursor<T, I: Iterator<T>, region Source>: Iterator<T>;
export fn take<T, I: Iterator<T>, region Source>(
cursor: ^I in Source,
amount: usize,
) -> ^TakeCursor<T, I> in Source;
export fn skip<T, I: Iterator<T>, region Source>(
cursor: ^I in Source,
amount: usize,
) -> ^SkipCursor<T, I> in Source;Both functions consume an explicit owning cursor and transfer it into an affine, non-Copy, non-Clone adapter in the same Source region. The adapter implements only the cursor next surface; it has no iter, into_inner, or remainder. Constructing either adapter performs zero pulls. A named input cursor requires an explicit move and cannot be resumed:
import std.iter;
fn main() {
let values: ^[i32] = try ^[1, 2, 3, 4];
let cursor = values.iter();
let tail = iter.skip(<-cursor, 1);
assert iter.count(<-tail) == 3 as usize;
// `cursor` was moved.
}take(cursor, amount) yields at most the first amount owners. With an amount of zero, its first next returns null without touching the inner cursor. Once it has yielded amount items, its next call returns null locally and never pulls item amount + 1. If the inner cursor ends first, take observes that one terminal null. In either case the adapter is then sticky: every later next returns null without consulting the inner cursor.
skip(cursor, amount) defers all work until its first next. It pulls and ordinary-drops up to amount owners, dropping each skipped owner before the following pull, and then yields the next owner. If the inner cursor ends while skipping or later while yielding, skip observes that one terminal null and becomes locally sticky. Dropping either adapter invokes no adapter next or adapter-provided drain loop; ordinary destruction destroys the owned inner cursor once, including any effects specified by that cursor's own destructor.
Wrapper order is semantic:
// For a cursor yielding 1, 2, 3, 4:
iter.take(iter.skip(source.iter(), 1), 2) // yields 2, 3
iter.skip(iter.take(source.iter(), 2), 1) // yields only 2An implementation must preserve that nesting, the exact order of pulls and item destruction, and sticky local exhaustion. It must not use a size hint to skip observable next or destructor effects. In particular, nested skips must not be fused through a + b unless representability and full equivalence are proven: the addition may overflow even though skip(skip(cursor, a), b) has valid sequential semantics.
Neither adapter exposes or consults a size hint.
There are no take or skip methods and no slice or generic-source overloads. A source selects its receiver role and produces the explicit cursor first, for example iter.take(values.iter(), 3). Fallible and lending cursors do not gain parallel adapter variants; their checked failures, loans, and any desired remainder are expressed with an explicit loop.
Transforming And Filtering Cursors
The accepted target exports two lazy, concrete-callback adapters:
export struct MapCursor<
T,
U,
I: Iterator<T>,
F: (&self, ^T) -> ^U,
region Source,
>: Iterator<U>;
export struct FilterCursor<
T,
I: Iterator<T>,
P: (&self, T) -> bool,
region Source,
>: Iterator<T>;
export fn map<
T,
U,
I: Iterator<T>,
F: (&self, ^T) -> ^U,
region Source,
>(
cursor: ^I in Source,
mapper: ^F in Source,
) -> ^MapCursor<T, U, I, F> in Source;
export fn filter<
T,
I: Iterator<T>,
P: (&self, T) -> bool,
region Source,
>(
cursor: ^I in Source,
predicate: ^P in Source,
) -> ^FilterCursor<T, I, P> in Source;F and P are structural callable-shape bounds, not erased callable types. Each adapter is monomorphized for the concrete closure or monomorphic named function and stores that concrete callback environment inline beside its inner cursor. Construction performs no pull or callback and introduces no implicit callable erasure, heap allocation, vtable, or runtime witness. The callback's structural properties, including Send and Sync, remain visible through the adapter type.
Both functions consume the cursor and callback. Named values make both transfers explicit:
let cursor = source.iter();
let mapper = [factor] (item: ^Item) -> ^Mapped {
return convert(<-item, factor);
};
let mapped = iter.map(<-cursor, <-mapper);
// `cursor` and `mapper` were moved.The &self receiver in each bound permits a stateful callback environment. A callback that only reads its captures also satisfies the bound because it needs the weaker self access. A callback requiring ^self does not satisfy it: the adapter may invoke its stored callback more than once.
var index: usize = 0;
let enumerate = [&index in _] (item: ^Item) -> ^Indexed {
let current = index;
index = index + 1;
return Indexed { index: current, item: <-item };
};
let indexed = iter.map(source.iter(), <-enumerate);MapCursor.next performs at most one inner pull. On an item it transfers that exact ^T owner to mapper exactly once and yields the returned ^U owner. The adapter no longer owns the input after the call. An inner null sets local sticky exhaustion and is returned without invoking the mapper. Every later pull returns null without consulting the inner cursor or callback.
FilterCursor.next may perform several inner pulls. For each ^T owner it calls predicate exactly once with a call-scoped shared loan. If the predicate returns true, the adapter yields that same owner. If it returns false, the adapter ordinary-drops the owner completely before its next inner pull. An inner null sets local sticky exhaustion; later pulls consult neither the inner cursor nor the predicate.
Both adapters are affine, non-Copy, non-Clone, and next-only. Breaking a loop over a named adapter preserves both its cursor position and callback environment state. Dropping an adapter performs no pull, callback, or drain; ordinary cleanup destroys the callback environment first and the inner cursor second. Traps do not unwind, so no post-trap cleanup or recovery is promised.
Callback effects are part of the operation order. An optimization must not cache, duplicate, speculate, parallelize, or reorder callback invocations, and must preserve these compositions exactly:
map(map(c, f), g): pull -> f -> g
filter(filter(c,p), q): pull -> p -> q only after p returns true
map(filter(c,p), f): pull -> p -> f only after p returns true
filter(map(c,f), p): pull -> f -> pThere are no map or filter methods, slice/source overloads, .iter() cursor copies, .count()/.collect() methods, into_inner/into_callback recovery accessors, or hidden source adaptation. A source produces its cursor explicitly before adaptation. No fallible, lending, async, or filter_map variant is added; use an explicit loop when failures, step loans, or different ownership policy must remain visible.
Enumerating An Owning Cursor
The accepted target exports exactly one zero-based enumeration adapter:
export struct EnumerateCursor<
T,
I: Iterator<T>,
region Source,
>: Iterator<(usize, ^T)>;
export fn enumerate<
T,
I: Iterator<T>,
region Source,
>(
cursor: ^I in Source,
) -> ^EnumerateCursor<T, I> in Source;enumerate consumes the explicit cursor and transfers it into an affine, non-Copy, non-Clone, next-only adapter in the same Source region. Constructing it performs no inner pull and introduces no adapter-selected allocation. A named cursor requires an explicit move:
let cursor = source.iter();
let indexed = iter.enumerate(<-cursor);
// `cursor` was moved.
for (index, item) in indexed {
consume(index, <-item);
break;
}
// A named adapter retains both its cursor position and its next index.
for (index, item) in indexed {
consume_rest(index, <-item);
}The counter starts at 0 as usize. Each nonterminal next performs exactly one inner pull. After receiving an owner, it saves the current counter, advances the stored counter with ordinary checked usize addition, and only then yields the exact owner as (current, item). An inner null does not advance the counter; it marks the adapter locally exhausted. Every later pull returns null without consulting the inner cursor.
If the increment overflows, ordinary checked arithmetic traps after the item has been pulled and before the pair is yielded. Traps do not unwind, so the adapter promises neither cleanup nor recovery after that point. This adapter does not introduce an alternate wrapping, saturating, or recoverable counter policy.
Dropping EnumerateCursor performs no pull or drain. Ordinary destruction destroys its inner cursor exactly once. Optimizations must preserve the exact pull, increment, yield, sticky-terminal, trap, and destruction order.
Adapter order remains semantic. Enumerating a filter assigns dense indices only to accepted owners, while filtering an enumeration assigns indices before the predicate and may leave gaps:
// Dense indices among accepted items.
let dense = iter.enumerate(iter.filter(source.iter(), predicate));
// Indices describe positions before filtering and may contain gaps.
let sparse = iter.filter(iter.enumerate(source.iter()), indexed_predicate);
// Mapping after enumeration observes each assigned index.
let mapped = iter.map(iter.enumerate(source.iter()), mapper);The corresponding observable order is fixed:
enumerate(map(c, f)): pull -> f -> checked increment -> yield pair
map(enumerate(c), f): pull -> checked increment -> f(pair)
enumerate(filter(c, p)): pull -> p -> [drop and retry | increment and yield]
filter(enumerate(c), p): pull -> checked increment -> p(pair) -> [drop | yield]An optimization must not commute these wrappers or otherwise change callback, increment, owner-drop, trap, or terminal-pull order.
There is no enumerate method, slice or generic-source overload, hidden source adaptation, cursor-copy iter, custom start, generic counter type, arithmetic variant, size hint, reset, or recovery accessor. Use a source's explicit role-selected iter first. A different initial value or arithmetic policy uses an explicit stateful map or loop.
Explicit Cursor Pairing
RFC 0042 removes zip and ZipIter from the accepted target without a replacement. Pairing two destructive, next-only cursors uses an explicit loop so the program owns the probe order and the fate of an unpaired owner:
let first = first_source.iter();
let second = second_source.iter();
loop {
let left_result = first.next();
if left_result == null { break; }
let left: ^A = left_result;
let right_result = second.next();
if right_result == null {
handle_unpaired_left(<-left);
break;
}
let right: ^B = right_result;
consume_pair(<-left, <-right);
}This policy cannot be provided symmetrically by an adapter over only next() -> ^T?. One cursor must be probed first. If it yields an owner and the other cursor then returns null, the first pull and its effects have already happened. Dropping, buffering, or returning that owner are distinct observable policies; buffering cannot undo either pull. The loop keeps that choice local and visible, and can instead choose right-first order or a different recovery policy.
The checked-in implementation remains transitional source inventory until the migration lands. Its ZipIter.next is left-first: it pulls the first cursor, then pulls the second only after receiving a left owner. If the second cursor returns null, the unpaired left owner is ordinary-dropped. ZipIter has no local terminal flag, so a later call probes the first cursor again and may consume and drop another left owner after the first observed null.
RFC 0042 adds no ZipCursor, zip method, cursor or slice/source overload, alias, remainder recovery, peek/lookahead wrapper, or exact-size interface. The separate checked-in EnumerateIter remains transitional source inventory; RFC 0043 replaces it with the accepted EnumerateCursor contract above.
For reference, the current transitional zip walks two inputs in lockstep, yields tuples that index with [0] and [1], and returns null after either side reports exhaustion:
import std.iter;
fn main() {
let ids: ^[i32] = try ^[1, 2, 3];
let scores: ^[i32] = try ^[10, 20, 30, 40];
// `zip` pairs both sides and stops at the shorter one; drain it with `for`.
var pairs: usize = 0;
var total: i32 = 0;
for pair in iter.zip(ids, scores) {
total = total + pair[0] + pair[1];
pairs = pairs + 1;
}
assert pairs == 3 as usize;
assert total == (1 + 2 + 3) + (10 + 20 + 30);
}Current Transitional Eager Terminals
The checked-in SDK still contains Copy-only borrowed-slice fold, reduce, sum, min, and max, plus Iter.fold. They are implementation inventory, not additional accepted APIs. RFC 0044 replaces the first two with the consuming cursor-only functions above and deletes the three specialized numeric/order helpers without aliases. RFC 0039 independently replaces the checked-in slice count with consuming cursor count; slices use .length.
Materializing A Cursor
The accepted std.iter target has no collect function or method. It also has no from_iter, to_slice, target-type inference, or recovery wrapper that hides partial progress. Materialization names the cursor and grows the result explicitly:
import std.mem.{ AllocError };
import std.iter;
fn materialize<T, I: iter.Iterator<T>>(cursor: ^I)
throws(AllocError) -> ^[T]
{
var out: ^[T] = ^[];
for item in cursor {
out = try (<-out).push(<-item);
}
return out;
}Each next and fallible push is visible. If a push fails, ordinary move and checked-error cleanup determines the disposition of the current item, the already built prefix, and the cursor remainder at that exact source location; std.iter does not silently choose a recovery policy.
For a borrowed [T] whose T is Copy, the existing slice.clone() operation is the direct way to request one owning copy:
let copy: ^[i32] = try values.clone();Composing Pipelines
Lazy adapters compose directly. A one-expression chain can thread one adapter into another and then drain it with consuming iter.count or a for loop. A named final cursor passed to count uses <-cursor; a reusable owning slice is built with a named final cursor and explicit fallible push.
import std.iter;
fn double(x: ^i32) -> ^i32 { return x * 2; }
fn is_even(x: i32) -> bool { return x % 2 == 0; }
fn main() {
let values: ^[i32] = try ^[1, 2, 3];
let cursor = iter.filter(
iter.map(values.iter(), double),
is_even,
);
var evens: ^[i32] = ^[];
for item in cursor {
evens = try (<-evens).push(<-item);
}
assert evens.length == 3;
assert evens[0] == 2;
assert evens[1] == 4;
assert evens[2] == 6;
}Current Transitional API Reference
The tables below inventory the checked-in implementation. They do not override the accepted cursor-only target above: Iterable, cursor iter() copying, and generic source overloads are removed by RFC 0037. Every checked-in collect symbol is transitional and removed from the accepted target by RFC 0038. The checked-in .count() methods and slice count overload are transitional and removed by RFC 0039; the target has only consuming free iter.count over an explicit owning cursor. Checked-in TakeIter/SkipIter, their methods, and their slice/source overloads are transitional under RFC 0040; the target has only affine TakeCursor/SkipCursor constructed by the two consuming free functions above. Checked-in MapIter/FilterIter, their erased-callback storage, cursor-copy methods, and slice/source overloads are transitional under RFC 0041; the target has only affine concrete-callback MapCursor/FilterCursor values constructed by the consuming free functions above. Checked-in ZipIter and both zip overloads are transitional under RFC 0042 and are removed from the target without a replacement; cursor pairing uses an explicit loop. Checked-in EnumerateIter, its cursor-copy iter, Iter.enumerate, and the slice/source overloads are transitional under RFC 0043; the target has exactly the consuming free enumerate function and affine EnumerateCursor described above. RFC 0044 additionally replaces checked-in fold/reduce entry paths with the two consuming cursor terminals above and removes iterator sum/min/max. RFC 0045 replaces checked-in Iter.any/all/find with the three borrowed-cursor functions above and removes position/nth/last/next_or; remaining fallible, lending, async, and other terminal families are intentionally not decided here.
Interfaces And Types
| Type | Kind | Methods | Description |
|---|---|---|---|
Iterator<T> | interface | next | Accepted owner-yield cursor interface; sticky exhaustion. |
ExclusiveIterator<T> | lending interface | next | Accepted infallible complete-element lending cursor interface with a fresh call-local Step loan and sticky exhaustion. |
Iterable<T, Iter> | transitional interface | iter | Checked-in source constraint removed from the accepted target by RFC 0037. |
SliceIter<T> | transitional region-typed struct: Iterator<T> | next | Checked-in copy cursor; target stateful std cursors are affine and non-Copy/non-Clone. |
Iter<T> | transitional region-typed struct: Iterator<T> | iter, next, next_or, skip, take, enumerate, fold, any, all, find, position, nth, last, count | Checked-in copy adapter; only its next protocol remains relevant. Cursor-copy iter, bounded/enumerate/count/fold methods, all predicate methods, and position/nth/last/next_or are removed by RFCs 0037–0045. |
EnumerateIter<T, IterT> | transitional region-typed struct | iter, next | Checked-in copy-capable iterator wrapper replaced by affine, non-Copy, non-Clone, next-only EnumerateCursor<T, I> under RFC 0043; its cursor-copy iter surface is not accepted. |
MapIter<T, U, IterT> | transitional region-typed struct: Iterator<U> | iter, next, collect, count | Checked-in erased-callback adapter replaced by affine, next-only, concrete-callback MapCursor<T, U, I, F> under RFC 0041; its copied state, iter, collect, and count are not accepted. |
FilterIter<T, IterT> | transitional region-typed struct: Iterator<T> | iter, next, collect, count | Checked-in erased-callback adapter replaced by affine, next-only, concrete-callback FilterCursor<T, I, P> under RFC 0041; its copied state, iter, collect, and count are not accepted. |
TakeIter<T, IterT> | transitional region-typed struct: Iterator<T> | iter, next, collect, count | Checked-in lazy adapter replaced by affine, next-only TakeCursor<T, I> under RFC 0040; its copied state, iter, collect, and count are not accepted. |
SkipIter<T, IterT> | transitional region-typed struct: Iterator<T> | iter, next, collect, count | Checked-in lazy adapter replaced by affine, next-only SkipCursor<T, I> under RFC 0040; its copied state, iter, collect, and count are not accepted. |
ZipIter<A, B, IterA, IterB> | transitional region-typed struct: Iterator<(^A, ^B)> | iter, next | Checked-in left-first adapter removed without replacement by RFC 0042. It has no local terminal state: after the right cursor returns null, later calls may keep pulling and dropping unpaired left owners. |
Functions
| Function | Signature | Description |
|---|---|---|
enumerate | enumerate<T: Copy>(items: [^T]) -> ^EnumerateIter<T, Iter<T>> | Checked-in transitional slice overload removed by RFC 0043; pass items.iter() explicitly. |
enumerate | enumerate<T, IterT: Iterator<T>>(items: ^IterT) -> ^EnumerateIter<T, IterT> | Checked-in cursor overload replaced by RFC 0043 with the region-preserving consuming free function returning affine EnumerateCursor<T, I>. |
enumerate | enumerate<T, IterT: Iterator<T>, Source: Iterable<T, IterT>>(items: Source) -> ^EnumerateIter<T, IterT> | Transitional generic-source overload removed by RFCs 0037 and 0043; produce and pass an explicit cursor. |
map | map<T, U, IterT: Iterator<T>>(items: ^IterT, mapper: ^(T) -> ^U) -> ^MapIter<T, U, IterT> | Checked-in erased-callback cursor overload. RFC 0041 replaces it with the region-preserving consuming free function over concrete F: (&self, ^T) -> ^U, returning MapCursor<T, U, I, F>. |
map | map<T: Copy, U>(items: [^T], mapper: ^(T) -> ^U) -> ^MapIter<T, U, Iter<T>> | Checked-in transitional slice overload removed by RFC 0041; pass items.iter() and a concrete callback explicitly. |
filter | filter<T, IterT: Iterator<T>>(items: ^IterT, pred: ^(T) -> bool) -> ^FilterIter<T, IterT> | Checked-in erased-callback cursor overload. RFC 0041 replaces it with the region-preserving consuming free function over concrete P: (&self, T) -> bool, returning FilterCursor<T, I, P>. |
filter | filter<T: Copy>(items: [^T], pred: ^(T) -> bool) -> ^FilterIter<T, Iter<T>> | Checked-in transitional slice overload removed by RFC 0041; pass items.iter() and a concrete predicate explicitly. |
take | take<T, IterT: Iterator<T>>(items: ^IterT, amount: usize) -> ^TakeIter<T, IterT> | Checked-in transitional cursor overload; RFC 0040 replaces it with the region-preserving consuming free function returning affine TakeCursor<T, I>. |
take | take<T: Copy>(items: [^T], amount: usize) -> ^TakeIter<T, Iter<T>> | Checked-in transitional slice overload removed by RFC 0040; pass items.iter() explicitly. |
skip | skip<T, IterT: Iterator<T>>(items: ^IterT, amount: usize) -> ^SkipIter<T, IterT> | Checked-in transitional cursor overload; RFC 0040 replaces it with the region-preserving consuming free function returning affine SkipCursor<T, I>. |
skip | skip<T: Copy>(items: [^T], amount: usize) -> ^SkipIter<T, Iter<T>> | Checked-in transitional slice overload removed by RFC 0040; pass items.iter() explicitly. |
zip | zip<A, B, IterA: Iterator<A>, IterB: Iterator<B>>(first: ^IterA, second: ^IterB) -> ^ZipIter<A, B, IterA, IterB> | Checked-in transitional cursor overload removed without replacement by RFC 0042. Its left-first probe can consume and drop an unpaired left owner when the right cursor ends. |
zip | zip<A: Copy, B: Copy>(first: [^A], second: [^B]) -> ^ZipIter<A, B, Iter<A>, Iter<B>> | Checked-in transitional slice overload removed without replacement by RFC 0042; pair with an explicit loop when ownership and probe order matter. |
collect | collect<T: Copy>(items: [^T]) throws(AllocError) -> ^[^T] | Checked-in transitional function removed by RFC 0038; it is not part of the accepted target. |
count | count<T: Copy>(items: [^T]) -> usize | Checked-in transitional slice overload. RFC 0039 replaces it with exactly count<T, I: Iterator<T>>(cursor: ^I) -> usize; slices use .length. |
fold | fold<T: Copy, A>(items: [^T], init: ^A, folder: (A, T) -> ^A) -> ^A | Checked-in transitional slice overload replaced by RFC 0044 with consuming fold<T, A, I: Iterator<T>, F: (&self, ^A, ^T) -> ^A>(^I, ^A, &F) -> ^A. |
reduce | reduce<T: Copy>(items: [^T], op: (T, T) -> ^T) -> ^T? | Checked-in transitional slice overload replaced by RFC 0044 with consuming strict-left reduce<T, I: Iterator<T>, F: (&self, ^T, ^T) -> ^T>(^I, &F) -> ^T?. |
sum | sum<E: Integer & Copy>(items: [^E]) -> ^E? | Checked-in transitional helper removed without replacement by RFC 0044; select identity and arithmetic explicitly with fold or reduce. |
min | min<E: Integer & Copy>(items: [^E]) -> ^E? | Checked-in transitional helper removed without replacement by RFC 0044; write ordering, tie, and losing-owner policy in reduce. |
max | max<E: Integer & Copy>(items: [^E]) -> ^E? | Checked-in transitional helper removed without replacement by RFC 0044; write ordering, tie, and losing-owner policy in reduce. |
Related Modules
Use std.collections for owned containers that expose iterators. Use the language Control Flow chapter for for loop behavior and tuple destructuring in loops.
Status
std.iter is the current public iterator facade. Its checked-in lazy adapters and eager terminals are transitional inventory. Adapter chains can be composed directly, as described in Composing Pipelines. For the full exported surface, see Standard Library Inventory. The checked-in Iterable module, copyable stateful cursors, cursor iter() methods, and generic source overload are transitional under RFC 0037. Every free or method collect is removed by RFC 0038; materialization uses a named cursor and explicit fallible push. RFC 0039 removes every .count() method and slice/source overload, retaining exactly consuming free iter.count over an explicit owning cursor. RFC 0040 replaces checked-in take/skip methods and source overloads with exactly two consuming free functions returning affine, next-only TakeCursor/SkipCursor values. RFC 0041 replaces checked-in erased callback storage, copied adapter state, and source overloads with exactly two consuming free functions returning concrete-callback MapCursor/FilterCursor values, and forbids adding cursor methods. The checked-in ZipIter and both zip overloads are removed without replacement by RFC 0042; an explicit loop is the canonical pairing policy. RFC 0042 adds no ZipCursor, method, source overload, alias, recovery, peek/lookahead, or exact-size abstraction. RFC 0043 replaces checked-in EnumerateIter, its cursor-copy iter, Iter.enumerate, and source overloads with exactly one consuming free enumerate returning affine, next-only EnumerateCursor. The counter starts at zero, advances with ordinary checked usize arithmetic after each successful pull and before yielding, and local exhaustion is sticky. There is no custom start, generic counter, arithmetic variant, size hint, or recovery surface. RFC 0044 replaces checked-in borrowed-slice fold/reduce and Iter.fold with exactly two free consuming cursor-only terminals over exclusively borrowed concrete callbacks. Both transfer exact accumulator/item owners in strict-left order and perform one terminal pull. Iterator sum/min/max are removed so empty, arithmetic, ordering, tie, and losing-owner policy is explicit. There is no method/source overload, callback erasure, short-circuit/recovery, fallible/lending, right, or parallel variant; other terminal families remain deferred. RFC 0045 replaces checked-in Iter.any/all/find with exactly three free functions that exclusively borrow a named cursor and concrete predicate. Short circuit preserves the exact advanced remainder, exhaustive completion observes one terminal null, boolean results destroy the deciding owner, and find returns it exactly. All seven transitional methods disappear; position, nth, last, and next_or have no standard replacement beyond explicit control flow and already accepted primitives. There is no consuming or source overload, remainder wrapper, callback erasure, or fallible/lending/ async family. RFC 0050 adds no generic iterator abstraction: SplitCursor and LinesCursor stay concrete structural borrowed-view cursors, so the owning-item adapters and terminals on this page do not apply to them.