match & exhaustiveness
A closed enum is only half the deal. The other half is match — the tool that takes a
Value apart — and its defining property: the compiler will not let you forget a case.
That guarantee is not a nicety; it’s load-bearing for a system with nine value shapes
dispatched on in dozens of places.
You’ll be able to: read a match over Value, and explain why exhaustiveness turns
“add a tenth shape” from a hunt-for-every-site nightmare into a compiler-guided chore.
A real match: type_name
Section titled “A real match: type_name”The engine constantly needs to ask a Value “what shape are you?” — for diagnostics,
for coercion, for error messages. Value::type_name answers it with one match over all
nine variants:
clinker-record ·value.rs ·type_name fn @47d2e12
pub fn type_name(&self) -> &'static str { match self { Value::Null => "null", Value::Bool(_) => "bool", Value::Integer(_) => "integer", Value::Float(_) => "float", Value::String(_) => "string", // ... one arm per variant, all nine }}Each arm names a variant; (_) says “I don’t care about the payload, just the shape.”
Why exhaustiveness is the point
Section titled “Why exhaustiveness is the point”A match must cover every variant — leave one out and the code won’t compile. That
sounds like nagging until you imagine the alternative. Value is dispatched on across the
whole engine: coercion, comparison, serialization, formatting. Now suppose a tenth shape
is added. With exhaustive match, the compiler immediately lists every single site
that must be updated — miss none, ship nothing half-handled. Without it, you’d be grepping
and praying.
This is the deep reason the closed enum from the last lesson pays off: closed set +
exhaustive match = the compiler is a complete, always-current checklist of “have you
handled every case?” (It’s also why reaching for a _ => catch-all is a smell in this
codebase — it silences exactly the warning you want.)
Break it on purpose
Section titled “Break it on purpose”> output appears here — press Run
It compiles and prints three type names. Now delete the Value::Float arm and Run.
The compiler stops you with an error — non-exhaustive patterns: &Value::Float(_) not covered — naming the exact case you dropped.
// quick check
Why is `match` being exhaustive a benefit when adding a new Value variant?
Exhaustiveness turns the compiler into a complete checklist: add a variant and every non-exhaustive match becomes a build error pointing at code that needs the new case.
Checkpoint
Section titled “Checkpoint”You can take a Value apart safely. Next: how the engine passes records around without
copying them — the ownership rules that make it fast.