docs: sync docs to the 229-test suite (CHANGELOG, CONTRIBUTING, README, CLAUDE)
Doc Sync Convention follow-through for the Tier 1-3 test work:
- CHANGELOG: new [Unreleased] section - the test-suite expansion (203 -> 229), Notes (thread safety, size_hint not directly assertable, portable Int extremes), and the infra-bound Tier 3 items tracked as future work (perf gate, cross-backend CI matrix, mutation testing).
- CONTRIBUTING: new test files in the Project Layout and Testing Guide table; test count 203 -> 229.
- README / CLAUDE: test count 203 -> 229; CLAUDE source map notes the white-box bimap_wbtest.mbt.
- also includes
moon fmtreflow of model_test.mbt / bimap_wbtest.mbt (no semantic change) somoon fmt --checkstays green.No public API change; pkg.generated.mbti unchanged (verified via moon info).
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
moonbit-bimap
A bidirectional map (bijection) for MoonBit — a port of Rust’s
bimapcrate / GuavaBiMap, extended with insertion-order preservation and index-based access (which neither Rust nor Guava provides).A
BiMap[L, R]keeps keys and values in one-to-one correspondence: you can look up left→right and right→left, and every insertion maintains the bijection invariant.Why a BiMap? (vs the built-in
Mapand vsindexmap)Mapindexmapget_index(i)Eq/HashsemanticsBiMapandindexmapsolve orthogonal problems — Bi = bidirectional (one-to-one, reverse lookup); Index = positional access. They share only the underlying hash table (as any two maps share arrays). This package is a fresh, dependency-free library, not a fork or rename ofindexmap.Features
get_by_left/get_by_right,contains_left/contains_rightinsertreturns anOverwrittenenum describing what was displaced (including the classic C4 collapse, see below)insert_no_overwritereturnsResult[Unit, (L, R)]iter()yields pairs in the order left keys were insertedget_index(i),get_index_of_left,get_index_of_right,first(),last()to_inverse() -> BiMap[R, L](a copy, not a live view)Debug,Default,Show,Eq/Hash(order-independent),ToJson, plus QuickCheckArbitraryInstallation
Add the dependency to your project’s
moon.mod:Then import it in the relevant
moon.pkg:The five insertion cases (C0–C4)
Inserting
(l, r)into a bijection has five sub-cases — the crux of a correct BiMap:insertreturnslenchangelnorrpresentNeither(l, r)already presentPair(l, r)lwas bound tor'≠r;rfreeLeft(l, r')rwas bound tol'≠l;lfreeRight(l', r)l→r'andl'→rboth existBoth((l,r'), (l',r))Gotchas
insertcan shrink the map (C4 collapse). Check the returnedOverwrittenif you need to know what was displaced.EqandHashare order-independent. ABiMapis a set of pairs; two maps with the same pairs in different insertion order are equal and hash the same. This is the opposite of the author’sindexmap, whoseEq/Hashare order-sensitive. BecauseHashcombines pair hashes commutatively, it is weaker against collision attacks — fine for a collection, but be mindful if using aBiMapas a key in another hash container.to_inverse()returns a copy, not a live view. Mutating the inverse does not affect the original (MoonBit’s ownership model favors copies over shared live views; this matches Rustbimap‘s method-based access rather than Guava’s liveinverse()).ToJsonkeys usel.to_string()(L : Show), soStringkeys serialize verbatim.from_arrayresolves duplicate pairs by “last wins” (viainsert), matching Rust’sFromIterator.lto a new right value does not movelto the end of the order. This is an intentional, order-preserving extension over Rust’s remove-then-reinsert behavior (see CHANGELOG).BiMapis not thread-safe. It is mutable and its iterators are fail-fast; concurrent reads/writes from multiple threads are undefined behavior. Use oneBiMapper thread, or guard shared access with external synchronization.API Overview
new(),with_capacity(n),from_array(pairs),default(),copy()len(),is_empty(),capacity()insert(l, r) -> Overwritten,insert_no_overwrite(l, r) -> Result[Unit,(L,R)]get_by_left(l),contains_left(l),remove_by_left(l) -> R?get_by_right(r),contains_right(r),remove_by_right(r) -> L?get_index(i),get_index_of_left(l),get_index_of_right(r),first(),last()iter(),lefts(),rights(),into_array()to_inverse() -> BiMap[R, L]Debug,Default,Show,Hash,Eq,ToJson,ArbitraryDesign
forward: L→R,backward: R→L) keep the bijection.orderarray +positionsmap tracks left-key insertion order, enabling index access without a second order structure on the backward table.put_pair/remove_by_left/remove_by_righthelpers that maintain the invariants:∀(l,r)∈forward ⟺ backward[r]==l, and five consistent counters.aurasuisui/indexmap(see below).Examples
Runnable example packages live in
cmd/:cmd/username_email— username ↔ email bidirectional lookup, iteration, and a rebindcmd/country_code— country name ↔ ISO code ("China" ↔ "CN"), reverse lookup, index access, and non-overwriting insertDevelopment
The five-step CI pipeline runs:
moon fmt --check→moon check→moon info && git diff --exit-code→moon test→moon build.See CONTRIBUTING.md for the architecture deep-dive and test conventions.
Known Issues
abortis not in-process testable. Mutating a map mid-iteration triggersabort, which the MoonBit test framework cannot catch as a passing assertion (a panicking test is reported as failed, not as “expected panic”). The version-snapshot + abort logic insrc/bimap_iter.mbtis verified by inspection and by a manual reproduction (documented there); all other iterator behavior is fully tested.Acknowledgements & Licensing
aurasuisui/indexmap(Apache-2.0).insert/insert_no_overwrite,Overwritten, bidirectional lookup) are ported from the Rustbimapcrate (MIT / Apache-2.0), with conceptual reference to GuavaBiMap(Apache-2.0). Order preservation and index access are original additions.License
Apache 2.0 — see LICENSE.
Built for the 2026 MoonBit Open Source Ecosystem Hackathon (August).