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
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.
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.
_ 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.
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.
_ instead of two, and the one that stays is
ADR-126’s own.match and if say otherwise with one word.examples/.match must cover every case, which is
ADR-137 §4’s open item and is about exhaustiveness rather than
about this arm’s spelling.else with a guard (else if x > 0), which is a shape nobody has
written and which the guard of ADR-137 D2 covers where it is
wanted.else arm that binds, which is what a bare name already does
(other => …) and is unchanged.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.