WEP: Iterator Reference Model
Align List iteration with Rust's iter / iter_mut / into_iter.
Context
The friction driving this WEP is that Wado's iterator methods diverge from Rust's, so Rust muscle memory misfires:
List::iter()returnsItem = T— it copies every element — the opposite of Rust, whereiter()yields&T. Solist.iter().any(...)silently copies.- There is no
iter_mut:&mut Listyields&T, and elements are mutated by index instead.
Reference iteration exists only via for-of &list (Item = &T), not through a
named method a Rust reader reaches for. The fix is to match Rust's surface.
Inventing a new divergence (e.g. making owned for-of list yield &T) would
only move the friction elsewhere, so it is out of scope.
Decision
Match Rust's conventions exactly:
iter()overList<T>yields&T(returnArrayRefIter, notArrayIter).iter_mut()yields&mut T(new), enabling in-place mutation (for let x of xs.iter_mut() { *x = f(*x); }) — sound with no borrow checker, riding on the write-back model (WEP: Reference Representation).into_iter()and ownedfor let x of listkeepItem = T(by value), matching Rust's owned iteration. Unchanged.for let x of &listkeeps&T. Unchanged.copied()for iterating a reference list by value:nums.iter().copied(). Implemented as an inherentArrayRefIter::copied() -> ArrayIter<T>(both share the same backing), not a genericIteratoradaptor — a genericimpl<I: Iterator<Item = &T>, T>can't yet resolveI::Itemto&Tin its body (associated-type-equality bounds aren't propagated), socopied()chains only directly offiter()today, not mid-chain afterfilter()/map().
Rejected — flipping owned for-of list to &T (and dropping the value
iterator): Rust's owned for x in v yields values, so this would add a new
Rust divergence, the opposite of the goal. It also turns snapshot-copy iteration
into live-reference iteration, a silent hazard when the body mutates the list.
Rejected — operator auto-deref: making &T read as T in operators is unsafe,
because == on &T today compares reference identity, not value (&a == &b is
false for distinct variables both holding 5). Auto-deref would silently turn
every &T == &T into a value comparison and remove identity testing via ==.
Primitives use copied() or *x instead. Reference value-vs-identity is a
separate proposal.
Consequences
- Removes the
iter()-copies footgun;.iter()now matches Rust (&T), anditer_mut/copiedfill the remaining Rust-shaped gaps. - The only breaking change is
iter()copy → reference (~136 call sites; the subset with|x: T|closures or value-consuming bodies needs|x: &T|/*x, the rest survive via field/method auto-deref). Ownedfor-ofis untouched, so no wide migration and no iterate-while-mutate hazard. - Operator semantics untouched (
==stays reference identity).
Status
- [x]
iter()yields&T(List::iter -> ArrayRefIter). - [x]
copied()onArrayRefIter; fixtureiter_ref_adapter_monomorph.wado. - [x] Migrated the breaking
iter()call sites. - [x]
&mutiteration for in-place elements:&mut List<T>yields&mut TviaArrayRefMutIter; fixtureiter_mut_inplace.wado. - [x] Reject
&mutiteration over replace-on-assign / unresolved-generic elements; fixtureiter_mut_forbidden.wado. - [x] Fixed a latent P0:
Fn<N,Ret>^Inspectdeduped by mangled name, not returnTypeId(&T/&mut Tcollide).
TODO
- [ ]
&mutiteration for replace-on-assign element types (primitive/enum/flags/variant/fn): needs the reference write-back model (write-back toxs[i]on every loop-exit edge — WEP-2026-06-13); rejected for now rather than silently dropped. - [ ] Public
iter_mut()method: redundant withfor ... of &mut xsuntil adapter chaining over&mut Tcomposes; add it then. - [ ] Generic
copied()on anyIterator<Item = &T>: blocked on propagating associated-type-equality bounds (Item = &T) into the impl body.
