I am playing around with a Windows message loop and ran across something that seems a bit off and was wondering if there is a better way of doing things?
Now the problem is that I messed up the use statements at the top of the file and there is no constant WM_MOUSELEAVE - but this still compiles as what Rust seems to be doing is creating a variable called
WM_MOUSELEAVE as a catch all in the final statement.
The only indication I can see that the compiler is giving me that there is something wrong is an unused variable WM_MOUSELEAVE - or when I add statements after the bad one, it warns about unreachable branches.
It took me a while to notice this as I have a lot of similar benign warnings in my in-progress code.
Is there a way I should structure the match code in this case to prevent these sort of accidents?
I can think of a few ways to deal with this problem; you can:
Use if/else instead of match, which doesn't pattern match and will therefore not interpret an unknown identifier as a variable binding.
Define a #[repr(u32)] enum WinMsg {...} of all the valid messages, and use WinMsg::RButtonUp et al. as your match arms. This will require a conversion from the u32 that's passed to winproc(), which is nontrivial; there are crates to help with this, but I'm not personally familiar with them.
Annotate your match statement with #[deny(unused_variables)] to turn the warning you're currently getting into a hard error.
I really wish this footgun would finally be fixed. Just add a lint, deny-by-default in the 2024 edition, that forbids the use of ALL_CAPS (edit: and CamelCase) catch-all patterns in matches. This would also fix the common problem of matching enum variants and forgetting to use the full prefixed name (or import the variants).
This is the best way in my opinion. Also often gives opportunity to make it look nicer, like winmsg::MOUSEMOVE which I prefer once I have to import the constants a lot.
Imho a more common error (particularly among new rustaceans) is to use an existing lower-case variable in one of the match arms. They expect that it will compare values, but instead it creates a new binding.
Yes, really the ideal here would be introduce let x patterns for explicitly binding a name and gradually lint in favor of that over bare names. But I think that ship has sailed, unfortunately.
Agreed, but it's more difficult to lint without causing (many?) false positives. Unless we add some sort of a "beginner mode" set of lints to the compiler. Myself, I wouldn't be against a deny-by-default (or at least warn-by-default) lint that disallows shadowing variables in match patterns though, but others might disagree.