ADR-145: A match’s Catch-All Arm Is else, Not _

Status: Superseded by ADR-291 — its D1-D3 are D16-D18 there Date: September 19, 2026 Target Version: Nikaia 0.0.29 Related: ADR-126 (_ is the ignore pattern, and the three positions it stands in), ADR-132 (else if, and what an else reads as), ADR-137 D1 (the six pattern shapes, whose examples write the arm), ADR-084 (what a keyword costs), Part I 3.4

1. Context

match code { 200 => "ok", _ => "other" }. The last arm is everything else, and it is written with a character borrowed from the ignore pattern — which says something different: ADR-126 is careful that _ means ignore a value that arrived, neither bind it nor use it, and the record even argues against calling it a wildcard for that reason.

A catch-all arm is not that. Nothing arrived to be ignored; the arm is the one taken when none of the others matched, which is exactly what else means one construct over — and this language has else, has else if (ADR-132), and reserves the word already.

2. Decisions

D1 — The catch-all arm is else, and _ in that position is refused.

match code {
    200 => "ok",
    404 => "not found",
    else => "other",
}

_ => … is NK1150, whose message names the replacement. Two spellings for one arm would be the state ADR-140 spent five migrations removing, one construct later.

D2 — _ keeps every other position it has.

ADR-126 D1’s three become two, and the two that stay are the ones where a value really did arrive and is being ignored: a position of a destructured tuple, and a parameter a shape dictates.

let (name, _) = pair()                      // stays
fn handle(event: Event, _: Context) { … }   // stays
xs.map fn(_, value) { value * 2 }           // stays

And it keeps the position inside a pattern that ADR-137 D1 will give it — (0, _) ignores a tuple’s second half — for the same reason: that is a value arriving.

D3 — It costs no word, and that is most of the argument.

else is already reserved and already means the branch taken when nothing before it matched. ADR-084 calls a keyword the most expensive thing a language adds; this adds none, and takes a character back from a meaning it did not have.

The lowering is Rust’s _, which is what the arm was before.

3. Consequences

4. What this does not decide

5. What is built

All of it. else is the first alternative of the pattern rule and a bare _ there is NK1150, from the parser, because that is where the two spellings meet and the message is the whole point: a match’s catch-all arm is written else.

The AST’s name changed with it. MatchPattern::Wildcard is MatchPattern::Otherwise — ADR-126 argues against wildcard for _ and the word was wrong for else twice over: nothing is being matched loosely, the arm is the one taken when nothing else was.

_ never parsed inside a pattern anyway, which is what made D2 free: the bindings of Message::Write(text) are an ident_list and NAME rejects a bare _ (ADR-126 §5), so the whole-arm position was the only one a match had. Taking it away leaves the record’s other two untouched and needs no case analysis.

Two arms in examples/, four blocks on the pages, and eleven test files. ADR-126 D1’s own example and ADR-137 D1’s — both records write the arm — read else now, with a line saying which record moved it.

Six tests in crates/nikaia/tests/match_else.rs.

This site is open source. Improve this page.