ADR-275: A Borrowed Left Side of ?? Beside a Jump Is a View Wherever It Stands

Status: Superseded by ADR-279 — its D1-D2 are D10-D11 there Date: October 3, 2026 Answers: what let s = c.signature ?? return false means where c is only borrowed - refused as a part of a loan taken (NK2106), or a view of the field. Met moving contracts::keeps’ lending rule into Nikaia (#125), where the program had to go round it; the owner chose option B. Related: ADR-259 D1 (?? lends its left side where the answer is read and takes it where it is kept), D3 (a fallback is lent only where a view of it lives as long as the answer); ADR-214 D2 (NK2106: a part of something lent handed to what keeps it); ADR-138 D1 (a fallback that jumps); ADR-213 and #297 (let found = m[k] binds a view of what the map keeps); ADR-250 D3 (a compiler’s gap is closed in the compiler, not gone round in the program)

1. Question

fn lends_in(contract: ref FnContract, at: i64) -> bool {
    let signature = contract.signature ?? return false
    …
}

ADR-259 D1 makes a let a position that keeps the answer, so the left side of ?? is taken. Here the left side is a field of contract, which is only borrowed: nothing can take a part of it, and the program was refused with NK2106. What does a let of a borrowed left side beside a jump mean?

ADR-259 was written for names the function owns, where taking is possible. It does not say what happens where the left side is itself only lent. Without an answer, a program wrote contract.signature?.clone() ?? return false and paid a copy, or went round through a helper function whose parameter is a ref Signature?.

2. How others do it

Language / tool Its answer What it costs there Source
Rust let Some(s) = &c.signature else { return false }; binds &Sig; matching on a place behind a reference binds by reference (“default binding modes”) nothing; the reference is checked by the borrow checker the reference’s let-else and match ergonomics, from memory
Swift guard let s = c.signature else { return false } binds a copy of the value (copy-on-write for large storage) a copy where the optimiser cannot avoid it from memory, not checked
Kotlin val s = c.signature ?: return false binds the same reference none; objects are references from memory, not checked
C# if (c.Signature is not { } s) return false; binds the reference for a class, a copy for a struct a copy for a struct from memory, not checked

What transfers. The languages that have borrowing as a rule (Rust) bind a reference where the matched place is behind one, which is the case here. The ones without it bind the same reference (Kotlin, C# classes) or copy (Swift, C# structs). None refuses the program.

3. Options, weighed

Option A — refuse, as today

let signature = contract.signature?.clone() ?? return false   // a copy

Option B — the answer is a view of the left side

let signature = contract.signature ?? return false            // `ref Signature`

Weighed

  D1 the special case pays, not the common one D2 the compiler works, not the developer D3 no cost for the common case D4 consistent with itself If it is wrong
A every such line pays a copy or a helper the developer writes the copy or the helper a copy of the field consistent with ADR-259 D1 read literally, but refuses the one meaning the line can have nothing breaks; programs keep going round it
B only a program that keeps the view past the loan writes .clone() nothing to write no copy the same as let found = m[k] (#297) and ADR-259 D3, whose test - a view of the answer lives as long as the answer - a jump meets, having no value a binding that was refused now compiles; undoing it refuses it again

4. Decision

B. It is the only meaning the line can have, and it is not a new rule: ADR-259 D3 lends where the fallback leaves the answer a view, and a jump leaves nothing else.

D1 — A borrowed left side beside a jump is a view wherever the answer stands.

Where the left side of ?? is a name or a field rooted in something only lent - a ref parameter, a for over a place, a let over one - and the fallback is a return, throw, continue or break, the answer is a view of the left side, in a reading position and in a let alike. The option is opened, not taken: .as_ref() below, .as_deref() for text. A left side the function owns keeps ADR-259 D1 unchanged.

D2 — A view kept past the loan is refused as any view is.

Returned, stored or handed to what keeps it, the view meets the refusals every view meets, with .clone() as the copy.

Why not A. It refuses a line whose only meaning is a view, and the way round it - a copy, or a helper whose parameter is the view - is exactly what ADR-250 D3 says a program should not have to write.

5. Consequences

This site is open source. Improve this page.