Rust: Irrefutable `if let` on a single-variant enum only produces () value

Created on 13 Jun 2019  路  8Comments  路  Source: rust-lang/rust

The following code (playground)

enum Enum{
    Variant(i32),
}

fn stuff(x: Enum) -> i32{
    if let Enum::Variant(value) = x {
        value
    }
}

gives the following error:

   Compiling playground v0.0.1 (/playground)
error[E0317]: if may be missing an else clause
 --> src/lib.rs:6:5
  |
6 | /     if let Enum::Variant(value) = x {
7 | |         value
8 | |     }
  | |_____^ expected (), found i32
  |
  = note: expected type `()`
             found type `i32`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0317`.
error: Could not compile `playground`.

which is weird, since the else clause would never be visited even if it was present.

The equivalent one-arm match

enum Enum{
    Variant(i32),
}

fn stuff(x: Enum) -> i32{
    match x {
        Enum::Variant(value) => value,
    }
}

compiles just fine (expectedly).

Adding an else { unreachable!() } branch to the if let is, of course, possible, but unelegant, and really shouldn't be needed, as it can be inferred at compile time.

A-diagnostics A-lint A-suggestion-diagnostics C-enhancement P-low T-compiler

Most helpful comment

I think this is too niche to deserve a special diagnostics (and I don't think we should substantially work on diagnostics for if until let_chains is implemented).

which is weird, since the else clause would never be visited even if it was present.

if let is defined by a desugaring:

if let PAT = EXPR BLOCK_IF [else BLOCK_ELSE]

becomes:

match EXPR {
    PAT => BLOCK_IF,
    _ => [ {} | BLOCK_ELSE ],
}

so what you really wrote is:

match x {
    Enum::Variant(value) => value,
    _ => {},
}

The block {} is typed at () which does not unify with value unless it is also typed at ().

All 8 comments

You can match irrefutably without if in this case:

fn stuff(x: Enum) -> i32 {
    let Enum::Variant(value) = x;
    value
}

though maybe we should give this special diagnostics.

I think this is too niche to deserve a special diagnostics (and I don't think we should substantially work on diagnostics for if until let_chains is implemented).

which is weird, since the else clause would never be visited even if it was present.

if let is defined by a desugaring:

if let PAT = EXPR BLOCK_IF [else BLOCK_ELSE]

becomes:

match EXPR {
    PAT => BLOCK_IF,
    _ => [ {} | BLOCK_ELSE ],
}

so what you really wrote is:

match x {
    Enum::Variant(value) => value,
    _ => {},
}

The block {} is typed at () which does not unify with value unless it is also typed at ().

I didn't know let Enum::Variant(value) = x; was a valid syntax. If I did, I probably wouldn't have filed the issue, because of course, in hindsight, that's how you write an infallible if let.

(With that knowledge in mind) I agree that it's only a diagnostics issue and no language change is needed here.
And I fully agree that it's a niche low-priority quirk that should maybe be fixed "someday". (I don't know what let_chains but even then I'd guess that this one is lower priority than that).

I think the appropriate thing to do here would be to lint on if let Irrefutable = foo {}, which would have clarified things.

But we do only after typechecking, and we don't if there are any errors before then. Quite a few lints have similar issues related to not happening early enough.

@estebank How do you know that the pattern is irrefutable before match checking in HAIR?

You can't, but you can write the lint in such a way that it triggers regardless. We're getting to typechecking, we should be able to detect irrefutability by then. It just can't use the standard linting machinery (like a few other lints already do to avoid being silenced by accident).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Robbepop picture Robbepop  路  3Comments

cuviper picture cuviper  路  3Comments

pedrohjordao picture pedrohjordao  路  3Comments

lambda-fairy picture lambda-fairy  路  3Comments

SharplEr picture SharplEr  路  3Comments