Check for unreachable at compilation time in Rust

80 Views Asked by At

I have enum consisting of a few variants:

enum Foo {
  A,
  B,
  C,
}

and i have a function, that has loop with if and match inside, all branches of which is diverging:

fn my_function() -> i32 {
  // some vars
  for ... in ... { // also may be `while` or `loop`
    // some code
    if some_condition {
      let foo: Foo = ...;
      match foo {
        Foo::A => { return 42 }
        Foo::B => { continue }
        Foo::C => {
          // do something with vars
          continue
        }
      }
      // this place is currently unreachable
      // and must **always** be **totally** unreachable
      // because the code after `if` must never execute in this(`some_condition`) case
    }
    // some code
  }
}

But with some future changes i may add new variant(s) to enum, so i want to check at compilation time, that that line is unreachable. I feel like this should be possible, but i don't know how to do it.

"Prior art":

  • unreachable! doesn't work for me, because it's a runtime check and it takes a lot of time before this code executes plus no guarantee that it will hit this unreachable because of rng involved in process of getting there.
  • compile_error! made for cfg flags, and in this case obviously gives error always, not depending of reachability of that place.
  • !-type - not sure if it's possible to use never-type to solve this problem.

If only there existed something like const_unreachable! or soft_compile_error! which would fill this exact scenario of giving compile error if and only if there exists execution path that leads to it...

So what are possible solutions (including rust nightly and any features) to this problem?

1

There are 1 best solutions below

1
On BEST ANSWER

On nightly, you can affect the result of your match to a variable with the "never" type:

#![feature(never_type)]

enum Foo {
  A,
  B,
}

fn main() {
    let x = Foo::A;
    let _: ! = match x {
        Foo::A => { println!("A"); return; }
        Foo::B => { println!("B"); /* Forgot `return` here */ }
    };
}

Playground

Gives:

error[E0308]: mismatched types
  --> src/main.rs:12:19
   |
12 | Foo::B => { println!("B"); /* Forgot `return` here */ }
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `!`, found `()`
   |
   = note:   expected type `!`
           found unit type `()` 

As pointed out by @rodrigo, until the never type gets stabilized the same can be achieved on stable Rust using std::convert::Infallible:

enum Foo {
  A,
  B,
}

fn main() {
    let x = Foo::A;
    let _: std::convert::Infallible = match x {
        Foo::A => { println!("A"); return; }
        Foo::B => { println!("B"); /* Forgot `return` here */ }
    };
}

Playground