v0.21.0 Phase 4 Review
Result
Phase 4 is complete and reviewed. Map<K, V> now exposes readonly, readwrite, and owning semantic entry iteration. Set<T> exposes the adopted construction, membership, mutation, equality, readonly iteration, and owning iteration surface over the same package-private table engine. Map and Set are exported by the synthetic prelude only after the complete standard declaration unit and native user-package tests qualify.
No public bucket, dense index, seed, marker value, probing operation, or representation-specific name was introduced.
Design Review
One storage authority
std/internal/table remains the sole owner of dense key/value synchronization, buckets, probing, growth, replacement, removal repair, seed retention, and allocator affinity. Set<T> stores Table<T, SetValue> and delegates every storage operation. Its private zero-sized marker is never public and does not have an independent hash or equality role.
Cursor contract
The table publishes three package-only cursors. Borrowed cursors contain paired dense views and one next index. Their remaining length is keys.len() - next_index. The mutable form yields only a readonly key and a readwrite value. The owning cursor retains the table owner and transfers paired dense-tail entries; its remaining length is the table's dense key length. Dropping it delegates unconsumed cleanup to that table owner.
The cursors intentionally expose only remaining and advance rather than implementing public iteration protocols. Public Map and Set iterator types are the sole Iterator and ExactSizeIterator adapters. This keeps storage mechanics independent from public item meaning and avoids relying on cross-module visibility of a package-private conformance.
Ownership and mutation
- readonly Map iteration yields copyable semantic entry values containing
&Kand&V; - readwrite Map iteration pairs
&Kwith&+V, so a key cannot be changed in place; - owning Map iteration transfers each
KandVonce, while early iterator destruction drops the unconsumed table suffix once; - Set has no mutable iterator or index operation;
- duplicate Set insertion retains the stored representative and destroys the incoming value;
- Set equality compares membership and length, not seed, capacity, dense order, or bucket layout.
Rejected Alternatives
- A second Set probing implementation was rejected because it would duplicate table invariants.
- Exposing table fields or buckets to Map/Set iterators was rejected because it would make the public collection layer depend on representation details.
- Pairing two independently public iterators in Map was rejected as the public source of truth; the table cursor owns the one paired-step invariant.
- Public
HashMap/HashSetaliases and compiler-known collection lowering remain prohibited. - Package-private table cursors implementing public iteration interfaces were removed during
qualification. Their concrete conformance was not a stable cross-module capability boundary and
unnecessarily coupled storage to
std/iter.
Qualification Evidence
The focused standard declaration-unit test validates contract-only roots, package visibility, direct source visibility, the reviewed dependency graph, declaration lowering, capability selection, body checking, and private implementation reachability.
The native associative collection test compiles and executes:
- readonly Map traversal and exact yielded count;
- mutable Map traversal that updates value fields while retaining readonly keys;
- full and early-abandoned owning Map traversal with owning
Stringvalues; - Set literal deduplication, duplicate insertion, removal, equality, readonly traversal, and owning traversal;
- prelude availability of the complete public collection types in an external package;
- the Phase 3 collision, replacement, removal, growth, zero-sized value, allocator, and failure-atomicity cases.
The final repository qualification record is the successful full test suite, formatting check, documentation build and link validation, and whitespace validation run immediately before the Phase 4 commit.