ADR-214: A Taking Has a Place in Its Statement and a Path in Its Value — and a Read Through the Brackets Warns About Nothing

Status: Superseded by ADR-293 — its D1-D3 are D31-D33 there Date: September 25, 2026 Target Version: Nikaia 0.0.190 Answers: the owner’s please fix the open points — ADR-213 §3’s three: two uses within one statement, a field handed over, and the unnecessary parentheses warning the README named Refines: ADR-212 D4 and ADR-213 D3/D4 (what a read after a taking is), ADR-094 D2 (keeps, for a part of a parameter), ADR-114 D4 (how a read through the brackets is written) Related: ADR-182 D2 (a slice read carries no *, which D3 extends to the parentheses)

1. Context

NK2702 and NK2105 ordered reads by statement: a taking counted against reads that started at or after the end of the statement it stood in. Three programs fell through that to rustc:

  program before
a keep(name, name) where keep keeps both use of moved value
b s.count() + s.count() the same
c xs.push(p.name) then println(p.name) use of moved value

and two more were found writing the probes for (c):

  program before
d fn f(p: P) { xs.push(p.name) } the parameter came out &P: cannot move out of p.name which is behind a shared reference
e xs.push(p.name) with p a ref parameter or a for binding the same

And every read through the brackets was written (*index::get(…)), so m[k] ?? 0 and let x = xs[1] carried rustc’s unnecessary parentheses warning — about a file nobody wrote, and named in the README as something a tester should expect.

2. Decisions

D1 — A read has a place in its statement.

Every read gets its order in the walk, and a taking remembers the order of the read it took. A read in the same statement with a later order is after it: keep(name, name) reads the second name after the first was handed over, and s.count() + s.count() walks the second after the first. The walk reads in the order the language below evaluates — arguments, operands and struct fields left to right, a receiver before its arguments. An expression walked twice counts once, by its address.

An assignment in the same statement gives back what it took, because it writes after it reads: name = name + "x" is a program.

D2 — A read has a path, so a part can be handed over and the rest kept.

A read of p.name is a read of that path, not of p. A taking of p.name touches a later read of p.name, of anything inside it, and of p as a whole; p.x is untouched, as the language below’s partial move allows. An assignment to p.name — or to p — gives it back.

Two places refuse or decide where this matters:

D3 — A read through the brackets carries no parentheses of its own.

*index::get(…) is written bare. Its parentheses are what makes (*get(…)).len() the length of the element rather than the element of a length, which only a postfix needs — and every postfix is written through one function (postfix_base), which now adds them for a read. As an operand, an argument, a let’s value or a cast’s, the * already binds tighter than anything around it.

3. What it costs

A read is a few more words in a list the checker already kept, and a path is a string built once per read. Nothing at run time.

4. What is built

All of it, in 0.0.190: the order and the path of every read, first_read_after shared by NK2702 and NK2105, NK2106, a part keeping its parameter in contracts::keeps, the parentheses moved into postfix_base, and the tests in crates/nikaia/tests/handed_over.rs and sequence_shapes.rs — including a program compiled with rustc’s warnings read back, so that a stray parenthesis fails a test rather than a tester.

This site is open source. Improve this page.