Issue #593: Decision: generic traits (trait Map[K, V]), and a standard Map trait the ecosystem can build on

Open and answer this issue on GitHub.

language, decision · opened 2026-10-12 · updated 2026-10-12 · 1 comment

What this is, from zero

A trait says what a type must be able to do, so different types can be used the same way (Part I 4.7). Nikaia’s traits cannot take type parameters. The owner would like a standard Map trait for the ecosystem: one lowest-common-denominator interface so libraries can accept “a map” and be written against a shared standard, as other languages offer (Rust has none for maps; Java Map, C# IDictionary, Go map built in, Swift Dictionary/protocols, Kotlin Map). This is not needed by the repository’s own code or self-hosting; the point is to let libraries share one interface while staying fast.

trait Map[K, V] {
    fn len(ref self) -> i64
    fn contains_key(ref self, key: ref K) -> bool
    fn get(ref self, key: ref K) -> V?
    fn insert(ref mut self, key: K, value: V)
    fn remove(ref mut self, key: ref K) -> V?
    fn clear(ref mut self)
}
fn total[M: Map[String, i64]](m: ref M) -> i64 { return m.len() }

What the user sees today (tried on 0.0.850)

Where it comes from

The owner, in the #589 session (October 12, 2026), after the question whether a BTreeMap can be passed where a HashMap is wanted (it cannot: NK1102; they are two types, and the hasher of a HashMap is chosen by the compiler per ADR-010 D5/D9). Nothing concrete needs it now: it is ecosystem design.

Facts that bound the answer

  1. Generic traits are the actual feature, and they are not map-specific: any shared interface (Iterator-like, Stream[T], Repository[T]) needs them. Parser, ledger (a trait entry with parameters), bound checking (NK1164 with arguments), lowering (Rust traits are generic already) all change.
  2. HashMap is two Rust types (TrustedMap / keyed) chosen by the compiler; after #581 a hidden type parameter at package boundaries. A Map trait implemented for HashMap must cover that without the user naming either. #581 should land first or define the interaction.
  3. The key bound: a Map[K, V] for both families needs K: Hash + Eq for one and K: Ord for the other. Without named bounds the trait could only be implemented for a fixed key type. See the std-bounds issue (filed beside this one).
  4. Views and keeps: get(ref self, key: ref K) -> V? returns something that may point into the map; the NK2302 rules (ADR-283 D25) make “a result into self only” the natural reading, but a trait method has no body, so D25 uses the receiver. ref mut results do not exist, so no get_mut; mutation goes through insert/update shapes.
  5. Performance: a trait bound is monomorphised (no dyn), so a generic function over M: Map[K, V] costs nothing over the concrete map. A dyn Map would cost an indirection and is not on offer.

Options

Risks

(a) is a large, irreversible language addition and the first step toward associated types/default bodies/dyn, each requested later (“the pressure of import rust”, import-rust-design.md §7). The language’s stance is fewer features on purpose; generics for traits are the one Rust feature most libraries cannot work around, which argues for (a) rather than (b). A trait shared by the ecosystem is also hard to change after libraries adopt it: a method added later breaks every impl.

Not prepared well enough to ask

Missing: how Go/Swift/Kotlin/Zig do shared collection interfaces and what they paid; the NK2302 rules for a generic trait method; the interaction with #581; a measured cost (none expected, monomorphised). Prepare that basis first; this issue is the place for it.

Recommendation

File and keep as decision; take the std-bounds issue first (smaller, needed by this), then prepare (a) with the interaction with #581. Do not build anything yet.

Related: #581, #585, #589 (built), #514, and the std-bounds and HashMap-key issues filed beside this one.

Comments

keywan-ghadami, 2026-10-12

The issues “filed beside this one” are: #591 (a HashMap/HashSet key that cannot be hashed passes the checker) and #592 (the std bounds Ord/Eq/Hash cannot be named). Take #592 first.


Generated by Claude Code

Back to Open Issues