ADR-098: A let Takes One Name, or a Flat Tuple of Them

Status: Superseded by ADR-291 — its D1-D5 are D1-D5 there Date: September 15, 2026 Target Version: Nikaia 0.0.8 Related: ADR-050 (overlap { … }, whose own line this makes a program), ADR-037 D6 and D7 (the sharing floor a destructure leaves alone), ADR-089 D2 (the parse-error note, whose shape this copies)

1. Context

let (user, rights, prefs) = overlap { … }          // Part I 8.1.2
let (tx, rx) = channel::bounded(100)               // Part II 12.5

Neither parsed. let takes one name, and a destructuring let is a form the specification uses twice, for two different constructs, and defines nowhere — Part I 2.1 introduces let with a name and says nothing about a pattern.

overlap { … } met this rather than made it: the construct is built and was reached by its tuple in the meantime (let r = overlap { … }, then r.0), which works and reads worse than what the page promises.

2. Decisions

D1 — A flat tuple of names, and not a pattern language.

match has patterns already and this is not them. What the two sites need is taking apart a tuple whose arity is known; a bigger answer would be a decision rather than this repair.

D2 — One field on one variant, not a second statement.

Stmt::Let’s name: Ident became names: Vec<Ident>, so every reader had to look — nineteen of them, which is the point. A second variant would be additive and cheaper, and a walk that has to remember it is a walk that will forget it.

What each reader decided is worth having in one place, because three of them chose to decline rather than to handle it:

reader with several names
the checker binds each name to the part at its index, or to ?
the emitter Rust’s own tuple pattern — nothing to translate
views marks every name a carrier, because a view reaches each part
contracts::order opaque: it carries one bound name, so it declines to reorder
contracts::sharing records no handle, leaving each part at ADR-037 D6’s atomic floor
dsl skips: a deferred block is one value and cannot be bound to a tuple

The last three are the same polarity three times: an analysis that cannot answer says nothing rather than guessing, and each of them fails safe — no reordering, no atomic taken away, no parameters claimed.

D3 — The widths are not compared.

A value whose type this compiler cannot see is ?, and refusing a destructure of one because it looks like the wrong width would refuse a correct program (Part III C.4). What a wrong width costs today is the language below saying so about a let the author did write, which is smaller and honest. The day a tuple’s width is known for certain is the day to refuse it here.

D4 — A written type is refused, NK1136.

let (a, b): T = … would have to say which name T is about and nothing decides that. Refused rather than ignored, because ignoring it takes something the author wrote and drops it.

D5 — A nested tuple is refused with a sentence.

It does not parse, and a bare parse error says a list of tokens — which does not tell a reader that the shape is what is missing. parser::let_names_note supplies the sentence, recognised from the rendering for ADR-089 D2’s reason: the failure happens after ( has matched, so the backend’s message is about the token it stopped at.

3. Consequences

Part I 8.1.2’s own line is a program. channel::bounded is not, and for a reason that has nothing to do with this: there is no channel (#259).

Nothing else in the tree moves — every let in examples/, tests/samples/ and crates/nikaia-std/src/ binds one name, so the whole change is reachable only by writing the new form.

#224’s entry closes — let takes one name, and the specification writes it taking several.

4. What this does not decide

5. What is built

let_names and let_name_tail in the grammar, parser::let_names_note, Stmt::Let::names, Checker::tuple_let with NK1136, the emitter’s tuple pattern, and the six readers above. Exercised by seven programs in crates/nikaia/tests/tuple_let.rs — the shape compiled and run, Part I 8.1.2’s own line, a part answering for the type it was taken from, NK1136, the nested refusal and its sentence, an unrelated bracket that must not get that sentence, and the single-name form untouched.

This site is open source. Improve this page.