Status: Superseded by ADR-293 — its D1-D4 are D27-D30 there
Date: September 25, 2026
Target Version: Nikaia 0.0.189
Answers: the owner’s yes to repairing the wall ADR-212 §5
found first — writing into a map keyed by an owned String reaches rustc —
and what was found next to it on the way
Builds: ADR-094 D2’s last paragraph (a caller that hands a
value to a keeping callee and uses it again is refused with the clone named as
the way out), decided and never built
Refines: ADR-080 D2 and ADR-114 D1/D4 (what goes
into a map’s brackets), ADR-207 D1 (the positions that keep a
literal)
Related: ADR-212 D4 (the takings NK2702 sees, whose
machinery this shares), ADR-055 §6 (NK2101, the same question
for a task), ADR-208 D2 (why a view kept needs .to_owned())
Every example keys its maps by ref String, so nothing in the corpus wrote a
map whose keys it owns. Written, each of these went to rustc:
| program | before | |
|---|---|---|
| a | m["a"] = 1 on a HashMap[String, i64] |
mismatched types: &str where the map keeps String |
| b | m[name] = 2, name: String |
the trait At is not implemented for String |
| c | m[name] ?? 0, name: String |
the same |
| d | ids[k] = … on a HashMap[i64, String] |
the key made a usize |
| e | ids[k] ?? "-" on a map of text |
&String: From<&str> is not satisfied |
| f | s = "b" for s: String, or ids[k] = "seven" |
NK1105: this is ref String — refused, though every other place that keeps text builds the literal |
| g | xs.push(name) then println(name) |
borrow of moved value |
(a) to (d) have one cause: the brackets wrap whatever is in them in
index::at, which exists for positions — it turns a number into a usize
and passes a view through — and a map’s key is not a position. (g) is not about
maps at all, but a map write is one of its places: the map keeps the key.
For a map whose keys are owned (not a view), the checker tells the emitter,
per key, what the brackets do, and index::at is not written:
NK1105) with ADR-208 D2’s sentence — the map needs text of
its own, and a copy is the program’s to write.&name, &(k + 1)), or
goes in as it is where it is a view already.A map keyed by views is left exactly as it was.
?? is a view.An assignment keeps what it is given, so s = "b" and ids[k] = "seven" build
the literal into a String, like a let, a field, a return and an argument
already did. And m[k] ?? "-" over a map of text is a view of text: the map
hands out its String by reference, the literal is a view, and the answer is
whichever of the two there is — nothing copied, nothing allocated
(index::Or<&str> for Option<&String>). A place that keeps the answer as a
String is then told so in ADR-208’s words.
NK2105.The places a value is given away rather than lent: an argument the callee
keeps (the & is not written — a method’s argument, or a function’s parameter
whose keeps says so; not a mut one, which is &mut below), a key or a value
written into a map or a list, a field of a struct being built (unless the field
is declared a view), an element of a list or a tuple literal, a let that names
it again, and an assignment. At each, a name whose type moves is recorded —
a number, a bool, a char, a view and a Seen stamp over any of those are
copied, which is NK2101’s rule for a task.
A later read on the same path is NK2105, with .clone() at the hand-over
as the way out. As for a sequence (ADR-212 D4), a name from outside
a loop or a lambda handed over inside it is refused where it is handed, unless
the loop gives it a new value or may leave on that turn; two arms of one if or
match are not one after the other; an assignment between gives it a value
again.
if ended == 0 { refuse(connection, 408, …) return } followed by
refuse(connection, 400, …) — the http example’s shape, three times in a row
— hands the connection over on a path that then leaves. The statement order
alone saw a second use. A walk or a hand-over inside an if or match arm or a
catch handler whose last statement is a return or a throw is asked about
reads up to the end of its statement and no further. NK2702 gets the same
answer, since it had the same blind spot. A break or continue is not this:
it leaves a branch and not the function.
The first two and the last are answered by ADR-214, 0.0.190.
f(name, name) where both keep it) are
not told apart, as for NK2702: reads are ordered by statement.xs.push(p.name)) is not recorded; only names are.
The language below allows moving a field out of a local it owns, and what is
refused afterwards depends on which field — a question for its own record.String, and HashMap::insert
(the language’s form is m[k] = v), are unchanged.??, which the
README already names, is unchanged.Nothing at run time: a key handed over or lent is what a Rust programmer writes
by hand, and index::at on a key was the identity or a type error. The checker
records one more list per body, read once at its end.
All of it, in 0.0.189: KeyForm and map_keys from the checker to the
emitter, the literal at an assignment, the view after ??, NK2105 at the
places D3 lists with the loop, lambda and branch rules, D4 for both NK2105 and
NK2702, and crates/nikaia/tests/handed_over.rs, whose programs are compiled
and run at both settings of user_parallelism.