& — Whether a Parameter Is Lent or Kept Is the Callee’s, Inferred Into the LedgerStatus: Accepted
Date: September 15, 2026
Related: ADR-005 D3 and §3 (the inference this extends, and
the clone that is still never inserted), ADR-312 D1 (a borrow is
not a clone), ADR-288 (the two spellings sync has, copied
here), ADR-281 D2 (a hull you can observe, you write — and why a
kept parameter is not one), ADR-023 D8 and
ADR-055 D1 (the ? and the .await the compiler already writes
off the ledger), ADR-083 (NK1131, which stays)
A parameter’s type says whether the callee looks at a value or keeps it, and
Nikaia has no overloading, so there is never a second callee for a & at the
call to choose between. Whether an argument is lent or handed over is
therefore a fact about the callee — derived from its body, true for every
caller — of exactly the kind the ledger records: sync is inferred from a
body and written down with an asserted spelling beside the inferred one
(ADR-288); the ? a failing call carries and the .await a
pausing one carries are written by the compiler off the ledger
(ADR-023 D8, ADR-055 D1); a method’s receiver is
auto-referenced. A caller that repeats the fact is doing the compiler’s
bookkeeping, and a caller that forgets it is refused by rustc — use of
moved value — about a file nobody wrote, which
Part III C.1 calls a bug in this compiler.
Across examples/, every & at a call or a loop head repeated what the
callee’s declaration said. And a for e in entries that consumes its list makes
entries.len() on the next line type-check clean and fail below.
&. Whether an argument is lent or handed over is the callee’s declaration’s, and the compiler writes the reference.Changed by ADR-184 D1: the borrow an expression writes is spelled ref x, so what is refused before an argument is a written ref.
fs::map(path), serve(db), into.merge(stats). What the argument becomes in
the emitted code — &path, &db, stats — is read off the callee’s
parameter (D2) and written by the emitter, the way the .await is.
A & written before an argument is refused, with a message that says the
callee already decides it. Not accepted-and-ignored: a redundant spelling that
parses is a second way to say one thing (ADR-277).
There is no callee without a parameter mode to read: a foreign crate is
described before it is called (ADR-290), so the & has
nowhere left to stand at a call.
keep, and the compiler checks it.Changed by ADR-views-3 D2: a parameter that keeps its argument says keep and a plain parameter is lent; the inference below checks the signature instead of writing it, and the ref T assertion is gone.
Changed by ADR-184 D1: the assertion is written ref T; what it asserts is unchanged.
Keeps means: stores it into something that outlives the call (a field of
the subject, a struct or collection handed back), returns it by value, gives
it to a task, or passes it to a callee whose own parameter keeps it. Inferred
over the call graph with a fixpoint for recursion — ADR-005
D3’s engine, asked one more question — and recorded per parameter in the
ledger as keeps, beside what the result points into.
Fail closed. A use the inference cannot resolve — a call nothing
describes, a method on a receiver of type ? — counts as keeping. The wrong
answer in the other direction emits a &T parameter whose body moves the
value, and that is rustc’s error about a file nobody wrote; the wrong answer
in this direction is a caller refused with this compiler’s words. Same
polarity as sync (ADR-288) and as ADR-010 D1.
The signature says it, the inference checks it. A parameter whose body
keeps the value is written keep x: T (keep self for the receiver); the
inference above no longer writes the mode, it holds the signature to it: a
body that keeps a parameter not marked keep is refused, and a keep the
body does not need is warned about (ADR-views-3 D2). There
is no ref T assertion left to write: a plain parameter already promises a
view. The ledger still records keeps, so a diff shows a signature’s promise
change.
Ordinary data is still moved where it is handed on — to a callee that
keeps it. ADR-312 D1 and ADR-005 §3 stand: no
clone is ever inserted, and a caller that hands a value to a keeping callee
and uses it again is refused with the clone named as the way out, in the
shape NK2101 already has for a task.
fn fill(mut out: Vec[i64]) is a parameter the callee changes in place, and
the caller’s value is what changes — &mut self’s rule, for every parameter.
It lowers to &mut T. The call, fill(xs), shows nothing, exactly as
xs.push(1) shows nothing; a language that hides mutation through a receiver
and shows it through an argument has two rules for one thing.
A callee that wants a mutable copy of an owned parameter writes
let mut v = x inside, which is what it writes for any other value.
for lends, and a let over a place is a view of it.for x in xs { … } iterates views of xs’s elements and leaves xs where it
was; xs.len() on the next line is a program. Iteration that takes the
elements away is written — for x in xs.drain() — because removing a name
from scope is the rare case and the one worth a word.
let s = totals.stations[name] and let name = config.name are views: the
language below refuses to move a value out of a container or out of a
borrowed field, so a move there was never what the line meant, and the
compiler writes the & it would otherwise have refused. let y = x over a
whole variable stays a move — it is a rename — and a let whose initialiser
is a call or a literal owns what it is given.
A & before the place in either position is refused as in D1.
This is the one semantic cost, and ADR-281 D2’s line is what it
has to answer: a hull you can observe, you write. A callee that keeps a
File runs its Cleanup at the end of the callee, not at the end of the
caller’s block, and the call site shows nothing.
Accepted, because the language already does this and calls it correct:
file.close() takes self (Part III 17.1) and
the call shows nothing; a struct literal that stores a handle moves it and
shows nothing. What is observable is written in the declaration and read
from the ledger, not spelled at every use. The difference from a hull is that
a hull changes a value’s type of ownership for the rest of its life, and a
kept parameter changes only where one owner ends.
What is owed is the narration: a change to a parameter’s keeps, on a type
whose teardown has an effect (Drop written in user code, or Cleanup), is
a ledger diff that names the callers whose cleanup moved — the NK2401 shape,
one more thing it narrates.
mut and keep on a parameter or the receiver; views in fields and results are inferred.Changed by ADR-views-3 D4: ref leaves the language outside extern "C"; a declaration writes the plain type, with mut or keep on a parameter, and views in fields and results are inferred (D5, D8).
Changed by ADR-184 D1 and ADR-251 D4: the view in a declaration is spelled ref T (ref self, ref mut self), and a result says what it points into as -> ref(a | b) T.
A declaration writes the plain type. A parameter or the receiver carries
mut when the body changes it and keep when the body keeps it
(ADR-views-3 D2, D3); ref stands only in extern "C"
(D4). A struct field or element that views a buffer is inferred from what is
stored in it (D5), and what a result points into is inferred or written
-> T from a | b (D8). NK1102’s and NK1104’s help texts say what the
callee’s declaration is, never write a mark about an argument.
mut parameter is given back before the function is left, and no call that can fail stands in between.Widened by ADR-views-1 D1-D3: the same rule holds for a let mut view of a place, given back before the view’s block is left.
A match arm that changes a part it binds takes the part (Part I 3.4), and an
assignment gives back what was taken (ADR-293 D31, D32). For a
mut parameter, and for the receiver ref mut self, which D3 makes the same
thing, what was taken belongs to the caller: the function may take a part
out, change it and put it back, but on every way out - its end, a return, a
throw - the parameter is whole again. Between the taking and the giving back
there is no throw and no call that can fail. Both are NK2108.
So no program ever sees a value with a part missing. A panic in between needs
no rule of its own: it ends the task, and what a crashed task left is never read
(ADR-327). What stands in the place while the part is out is the
lowering’s choice and not observable.
Rejected:
mut parameter at all, so a tree is only rebuilt by
value (fn f(e: E) -> E). Simpler, but it makes the in-place form of
Part I 3.4 a refusal for parameters, and below it every node is allocated
again unless the lowering learns to reuse the old node’s memory.What a library author gets. A signature is a smaller thing to write and a
consumer reads the mode off the ledger, the way they read sync and throws.
A callee that starts keeping its argument breaks the callers that used the
value again — loudly, with this compiler’s words and the ledger’s narration —
which is ADR-005 D3’s action-at-a-distance answered the way it
already is for a borrow that lengthens.
What a reader loses. At fill(xs) nothing says xs changes; at
keep(file) nothing says the file is flushed inside. D3 and D5 put both in
the declaration, and a reader who wants them at the call has the ledger. That
is a real cost and it is the same cost method receivers already carry.
What this costs at run time: nothing. A view is a pointer and a move is a copy of a header; the emitted Rust is the signature a person would otherwise write, written by the compiler instead.
let that later escapes. let s = totals.stations[name] followed by
return s is a view returned, which ADR-005 D3 and
ADR-283 already answer.One function answers both halves. contracts::keeps::lends is what the
emitter reads to write the declaration and what the checker reads to write the
argument, because the two disagreeing is a double reference or a moved value
in the language below.
What is not lent, each the polarity spent rather than a gap: a parameter the
column says is kept; a value that copies, where a reference buys
nothing; an argument that is already a view; and a method’s argument —
which entry acc.record(m) goes to is the type checker’s answer and the
emitter has none (ADR-288), so a call the checker does not walk
would hand a value into a reference.
A for lends through .iter(), which reads the same through any number of
references; xs.drain() is the written form of taking the elements, lowered as
into_iter. A let over a place that copies does not lend, because a
reference there is a borrow held across whatever writes the same place.
A written ref before an argument or a for’s list is NK1137, raised
after the type fit and never on a type nothing pinned: an argument of the wrong
type is NK1102, not a lesson in punctuation.
A mut parameter (D3) lowers to &mut T and the call gains its &mut;
keeps::lends withholds its own claim on such a position. A body that changes
an owned parameter it did not declare mut is NK1138, raised only where
the change is certain.