Why this fires¶
match is being used on a closed type, but its unguarded arms do not cover
every possible value. Zolo checks bool, enums (including their payloads),
optionals with closed payloads, closed unions, and tuples/records whose fields
are closed. A guarded arm does not prove coverage because its guard can evaluate
to false.
enum State { Ready, Busy }
fn code(state: State, enabled: bool) -> int {
match state {
State::Ready if enabled => 1,
State::Busy => 2,
}
// error[TE829]: non-exhaustive match; missing `State::Ready`
}Without this error, the unmatched path would fall through to the backend's
match fallback and could produce nil even when the surrounding expression
has a non-nil type.
Fix it¶
Add an unguarded arm for every missing value:
match state {
State::Ready if enabled => 1,
State::Ready => 0,
State::Busy => 2,
}When all remaining values intentionally share one result, use an unguarded wildcard or binding arm:
match state {
State::Ready if enabled => 1,
_ => 0,
}Notes¶
_, a bare identifier, andname @ _cover all remaining values only when the arm has no guard.- Alternatives in an or-pattern contribute coverage independently.
- Product correlations are preserved:
(true, _) | (_, true)still misses(false, false). - An open payload inside a finite constructor must be covered by a binding or
wildcard. For example,
Some(1)does not cover everySome(int)value. - Open or gradual scrutinee types such as
anyare not rejected by TE829. - Large products and recursive enums are checked symbolically rather than by expanding every value. Recursive constructors count as inhabited only when a finite path reaches a base constructor.