Conditional compilation with a if let enum matching which consists of one item


#1

Originally the post was written on stackoverflow.

I have the following enum:

pub enum Game {
    Match(GameWorker),
    #[cfg(feature = "cups")]
    Cup(CupWorker),
}

So, this enum consists of one item if cups feature is disabled. The code below with match compiles okay but in place where I use if lets on matching this enum there is a error:

Working match:

fn clear(&mut self, silent: bool) {
    match *self {
        Game::Match(ref mut gm) => gm.clear(silent),
        #[cfg(feature = "cups")]
        Game::Cup(ref mut c) => c.clear(silent),
    }
}

if let which leads to a compile error:

let m: &mut Game = Game::Match(...);
if let Game::Match(ref mut gamematch) = *m {
    // ...
}

Error:

error[E0162]: irrefutable if-let pattern
   --> src/game.rs:436:32
    |
436 |                         if let Game::Match(ref mut gamematch) = *m {
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ irrefutable pattern

Minimal example

Is there a way to allow such if lets ? I like this construction but somewhy it is not allowed to use it, I don’t understand why. As shown above, match construction works okay in the same case. In my personal opinion here should be a silenceable warning instead of error. What do you guys think here?


#2

I found that the original RFC 160 did say this:

Contrary to a let statement, the pattern in the if let expression allows refutable patterns. The compiler should emit a warning for an if let expression with an irrefutable pattern, with the suggestion that this should be turned into a regular let statement.

An irrefutable if let seems not much different than if true, which is certainly allowed. Similarly an irrefutable while let is just a loop. I wonder when it turned into a hard error?


#3

The while let RFC 214 says:

Just as with if let, an irrefutable pattern given to while let is considered an error. This is largely an artifact of the fact that the desugared match ends up with an unreachable pattern, and is not actually a goal of this syntax. The error may be suppressed in the future, which would be a backwards-compatible change.

So I suspect this was made strict when implementing if let after its RFC was accepted, when this “artifact” was realized.


#4

Since match foo { A => {}, B => {}, _ => {} } triggers just a deny lint, if A and B are the only variants, this should probably also be a deny lint instead of a hard error.

Should be a trivial enough change in the compiler. I don’t think this would even need an RFC, since if let is just sugar for match, so it should mirror the changes.

Note that your match and your if let are not equivalent. The if let is actually

match *m {
    Game::Match(ref mut gamematch) => { /* ... */ }
    _ => {},
}

while your manual match, with the feature disabled is

match *self {
    Game::Match(ref mut gm) => gm.clear(silent),
}