Rust will `never` do that!


That is, the Rust never type (!).

Everything is an expression

Rust is kind of an “everything is an expression” type of language. An expression is something that produces a value. Not everything is an expression, but basically everything that is in a Rust body (executable code) is an expression. Actually, one thing that isn’t are locals, like:

let x = 5;

That is its own statement! But you can have an expression with a let statement, just take:


if let Some(num) = func_that_returns_option() {}

The let statement in there isn’t a local, it’s a let expression.

Let’s go over a few other things that are also expressions:

{ 1 }
loop { break 1; }
if true { 1 } else { return; }

This last one is what we’ll be interested in.

The never type

So if we look into the last example I gave, you know the if block evaluates to 1. But what about the else block? return; doesn’t produce a value, it actually leaves the function. So what is its type?

That’s right, the never type!

The point of the never type is to say “this computation will not complete.” That way, if you assign something to that value:

let x = if true { 1 } else { return; }

The type of x is based on the if block. But, that doesn’t mean the never type is ignored. How the compiler figures that out is never can be coerced into any type. That means, when trying to infer the type for x, the compiler sees the if block has type i32. Then the else block has type never, so to make it consistent, it says “never can convert to i32” and it’s all okay!

What does this mean for the user?

Well, not much. It’s mostly a fun compiler intricacy. But, if you want to see it mentioned in your diagnostics, try the new let else syntax (currently only available with nightly). This syntax lets you use a pattern for a let binding that may not always be true, like:

let Ok(x) = returns_result() else { warn!("This is bad!"); return; };

So, if returns_result returns Ok, then we assign x to the value in Ok. But, if it’s not, we warn and return. Pretty easy!

The type of that else block has to be never, though.

So try making it not, with this minimal example:

letelse.rs

#![feature(let_else)]

fn opt() -> Option<i32> {
    Some(1)
}

fn main() {
    let Some(x) = opt() else { 1 };
}

This will error:

error[E0308]: `else` clause of `let...else` does not diverge
 --> letelse.rs:7:30
  |
7 |     let Some(x) = opt() else { 1 };
  |                              ^^^^^ expected `!`, found integer
  |
  = note: expected type `!`
             found type `{integer}`
  = help: try adding a diverging expression, such as `return` or `panic!(..)`
  = help: ...or use `match` instead of `let...else`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.

We expect that else block to diverge, so it has to be the never type!

Cool.

Rust will never return from that.