It's less of a designed-in behaviour and more of a consequence of other decisions.
A match
arm takes a series of patterns, and evaluates whichever body follows from the first pattern in the list that accepts the value (msg
, in your case). "Pattern" is a very broad category - it includes literal values (2
), but also compound values, and, critically, placeholders.
Placeholders are useful: they let the body of the match arm operate on parts of the matched value. Unfortunately, they share syntax with other kinds of language item: the token WM_DESTROY
may be a placeholder, but it may also be an existing variable, a constant, a type name, a trait name, a function name, or any of a few other things. The distinction rests on what's in scope at that point: if WM_DESTROY
is a new symbol, then it's treated as a placeholder and defines a new symbol for the resulting variable binding, while if it's a symbol already in scope, then it is that symbol.
This is a known hazard with match
statements and other pattern-matching operations, which is why clippy
has a lint for it, but it's pretty damned near impossible to fix without seriously hampering the ergonomics of pattern-matching for at least some common purposes, and without breaking a lot of existing code.
As for utility, a pattern consisting of a bare placeholder is useful for catch-all patterns, usually at the end of a match
:
match msg {
// imagine several arms here for various window messages
WindowsAndMessaging::WM_DESTROY => { todo!("destroy window"); },
other => {
eprintln!("Ignoring unnexpected window message: {other:#?}");
}
}
While you might not leave that code in place permanently, a catch-all arm like that lets you see what your program is actually doing during development, and might be crucial to figuring out how the documentation lines up with reality. WM_DESTROY
, when interpreted as a placeholder, has exactly the same "purpose," only in your program, it's being interpreted that way by accident rather than by intent.