ADR-191: ?? Joins Two Views, And A ?. Takes One Of A Binding And Not Of A Temporary

Status: Superseded by ADR-278 — its D1-D3 are D19, D20 and D21-D22 there; its D4, a test migration, is not carried Date: September 22, 2026 Target Version: Nikaia 0.0.142 Answers: open-decisions.md’s what does ?? mean when its left side is a view? — option A, the one that page recommended and the owner took Closes: the second and third thirds of ADR-113, narrowing #192 to one shape Related: ADR-190 D2 (which filed the question), ADR-008 D2 and D5 (the lattice, and no inserted copy), ADR-184 D2 (a view of String is str below), ADR-094 D4 (a let over a place is a view — the same sentence one construct away)

1. Context

ADR-190 D2 established that the last third of ADR-113 waits on one question rather than on a representation: what ?? means with a view on its left. The options were A (join two views, refuse the mixed form), B (leave it) and C (widen to the fallback, inserting a copy). The owner took A.

And building A in its literal form opened a new hole, which is why this is a record and not a patch. Measured:

let name = or(find(1).as_ref().map(|it| it.name.as_str()), || "nobody");
//            ^^^^^^^ temporary value is freed at the end of this statement

A view of a temporary dies at the ;. Binding it is rustc’s temporary value dropped while borrowed about a file nobody wrote — the very class ADR-113 exists to close, reopened one shape over.

And the obvious guard was the wrong one. emit::is_a_place asks whether an expression can be written to, which a ?. reach can; find(1)?.home?.city passes it at every link and roots in a call. What this needs is a different question.

2. Decisions

D1 — A ?. takes a view of a receiver that roots in a binding, and leaves a temporary owned.

check::roots_in_a_binding follows the chain down — a name, a field of one, an index of one, a ? or a cast over one — and answers on the root. A root that is a name is storage the enclosing block owns; a root that is a call is a temporary.

And that keeps ADR-113 D1’s promise where it means anything. D1’s sentence is user is usable on the line after user?.name — a temporary has no line after and no name to be usable by, so there is nothing for the rule to be about there.

D2 — ?? joins two views, and a fallback that owns is NK1185.

A view on the left and an owned value on the right asks the operator to hand back one value that is both. Three ways exist and the language has ruled two out:

The help carries the way out the program can take — ?? "…" reads the same and costs nothing — which is C.2’s rule that a way out that cannot be taken is not one.

.to_owned() and .to_string() are read by name, because no ledger describes either and the one spelling this whole question is about — ?? "nobody".to_owned() — would otherwise type as ? and walk past the refusal. That is the same fact contracts::tether::makes_a_buffer already reads the same way: all four entries of either name hand back owned text.

D3 — What is left is the temporary receiver, and it wants a lowering rather than a state.

rustc’s own help for it is consider using a let binding to create a longer lived value, and that is a lowering this emitter could write — it is ADR-185 D2’s trick one construct over.

It is not free, which is why it is not here: a ??’s right side is lazy, so hoisting a receiver out of a ?? find(1)?.name would run find(1) where today it does not. Deciding where a hoist is safe is work of its own, and it is a lowering question — not the tether, and not a state (ADR-190 D1).

D4 — The migration is what the recommendation said it was.

One line, in crates/nikaia/tests/nullable.rs: u.home?.city ?? "nowhere".to_string() becomes ?? "nowhere". Nothing in examples/ or benches/ writes the shape at all — the corpus’ one ?. reaches an i64. The .to_string() that stood there was only ever matching a left side that used to be owned.

3. Consequences

4. What this does not decide

5. What is built

All four, at 0.0.142.

This site is open source. Improve this page.