Reputation: 927
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
Reputation: 30021
The type of return 42
is !
:
break
,continue
andreturn
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