Issue
I have read several answers on SO already, and gathered these use-cases:
- When a function
panic!
s - When a function has an infinite loop in it
But it is still unclear to me why we need to define the function like this:
fn func() -> ! {
panic!("Error!");
}
if it will work the same way as this (without the exclamation sign):
fn func() {
panic!("Error!");
}
and at the same time, why do we need to use !
in functions with infinite loops? It look like this signature doesn’t bring any real usage information.
Solution
The main difference between these signatures boils down to the fact that !
can coerce into any other type, and thus is compatible with any other type (since this code path is never taken, we can assume it to be of any type we need). It’s important when we have multiple possible code paths, such as if-else
or match
.
For example, consider the following (probably contrived, but hopefully clear enough) code:
fn assert_positive(v: i32) -> u32 {
match v.try_into() {
Ok(v) => v,
Err(_) => func(),
}
}
When func
is declared to return !
, this function compiles successfully. If we drop the return type, func
will be declared as returning ()
, and the compilation breaks:
error[E0308]: `match` arms have incompatible types
--> src/main.rs:8:19
|
6 | / match v.try_into() {
7 | | Ok(v) => v,
| | - this is found to be of type `u32`
8 | | Err(_) => func(),
| | ^^^^^^ expected `u32`, found `()`
9 | | }
| |_____- `match` arms have incompatible types
You can also compare this with definition for Result::unwrap
:
pub fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
}
}
Here, unwrap_failed
is returning !
, so it unifies with whatever type is returned in Ok
case.
Answered By – Cerberus
Answer Checked By – Jay B. (BugsFixing Admin)