I have read several answers on SO already, and gathered these use-cases:
panic!sBut 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With