zombiesauce
zombiesauce

Reputation: 927

Shouldn't the `return` statement return `!` (never type)?

Example code snippet:

fn foo() -> i32 {
    let a = return 2;
    a + 1
}
fn main() {
    println!("{}", foo());
}

I would expect that since a will never actually get assigned anything, its type should be !. However the compiler tells me that its type is in-fact () (the unit type). This struck as weird to me. What could be the reason behind this?

Upvotes: 2

Views: 532

Answers (1)

mcarton
mcarton

Reputation: 30021

The type of return 42 is !:

break, continue and return expressions also have type !. For example we are allowed to write:

#![feature(never_type)]
let x: ! = {
    return 123
};

From https://doc.rust-lang.org/std/primitive.never.html.

But one of the characteristic of ! is that it can be coerce to a value of any type:

fn foo() -> i32 {
    let a: String = return 2;
    42
}

fn main() {
    println!("{}", foo());
}

This is what enables things like

let num: u32 = match get_a_number() {
    Some(num) => num,
    None => break,
};

(from the same page).

Both branches must have the same type. num is clearly u32, and break is !. Both can then have the same type u32 by coercing break to u32.

This is perfectly fine because a value of type ! can never exist, so the compiler can "convert" it to any value of any other type.

Where the confusion arises in your example is that the compiler will claim "error[E0277]: cannot add i32 to ()". This is probably for historical reasons. ! didn't use to exist as such in the Rust 1.0 days. Over time it became more of a first class citizen, but some special cases were required for backwards compatibility where ! will be treated as () over any other type.

Upvotes: 6

Related Questions