Primitive Type never
never_type
#35121)Expand description
The !
type, also called “never”.
!
represents the type of computations which never resolve to any value at all. For example,
the exit
function fn exit(code: i32) -> !
exits the process without ever returning, and
so returns !
.
break
, continue
and return
expressions also have type !
. For example we are allowed to
write:
#![feature(never_type)]
let x: ! = {
return 123
};
RunAlthough the let
is pointless here, it illustrates the meaning of !
. Since x
is never
assigned a value (because return
returns from the entire function), x
can be given type
!
. We could also replace return 123
with a panic!
or a never-ending loop
and this code
would still be valid.
A more realistic usage of !
is in this code:
let num: u32 = match get_a_number() {
Some(num) => num,
None => break,
};
RunBoth match arms must produce values of type u32
, but since break
never produces a value
at all we know it can never produce a value which isn’t a u32
. This illustrates another
behaviour of the !
type - expressions with type !
will coerce into any other type.
§!
and generics
§Infallible errors
The main place you’ll see !
used explicitly is in generic code. Consider the FromStr
trait:
trait FromStr: Sized {
type Err;
fn from_str(s: &str) -> Result<Self, Self::Err>;
}
RunWhen implementing this trait for String
we need to pick a type for Err
. And since
converting a string into a string will never result in an error, the appropriate type is !
.
(Currently the type actually used is an enum with no variants, though this is only because !
was added to Rust at a later date and it may change in the future.) With an Err
type of
!
, if we have to call String::from_str
for some reason the result will be a
Result<String, !>
which we can unpack like this:
#![feature(exhaustive_patterns)]
use std::str::FromStr;
let Ok(s) = String::from_str("hello");
RunSince the Err
variant contains a !
, it can never occur. If the exhaustive_patterns
feature is present this means we can exhaustively match on Result<T, !>
by just taking the
Ok
variant. This illustrates another behaviour of !
- it can be used to “delete” certain
enum variants from generic types like Result
.
§Infinite loops
While Result<T, !>
is very useful for removing errors, !
can also be used to remove
successes as well. If we think of Result<T, !>
as “if this function returns, it has not
errored,” we get a very intuitive idea of Result<!, E>
as well: if the function returns, it
has errored.
For example, consider the case of a simple web server, which can be simplified to:
loop {
let (client, request) = get_request().expect("disconnected");
let response = request.process();
response.send(client);
}
RunCurrently, this isn’t ideal, because we simply panic whenever we fail to get a new connection. Instead, we’d like to keep track of this error, like this:
loop {
match get_request() {
Err(err) => break err,
Ok((client, request)) => {
let response = request.process();
response.send(client);
},
}
}
RunNow, when the server disconnects, we exit the loop with an error instead of panicking. While it
might be intuitive to simply return the error, we might want to wrap it in a Result<!, E>
instead:
fn server_loop() -> Result<!, ConnectionError> {
loop {
let (client, request) = get_request()?;
let response = request.process();
response.send(client);
}
}
RunNow, we can use ?
instead of match
, and the return type makes a lot more sense: if the loop
ever stops, it means that an error occurred. We don’t even have to wrap the loop in an Ok
because !
coerces to Result<!, ConnectionError>
automatically.
§!
and traits
When writing your own traits, !
should have an impl
whenever there is an obvious impl
which doesn’t panic!
. The reason is that functions returning an impl Trait
where !
does not have an impl
of Trait
cannot diverge as their only possible code path. In other
words, they can’t return !
from every code path. As an example, this code doesn’t compile:
But this code does:
use std::ops::Add;
fn foo() -> impl Add<u32> {
if true {
unimplemented!()
} else {
0
}
}
RunThe reason is that, in the first example, there are many possible types that !
could coerce
to, because many types implement Add<u32>
. However, in the second example,
the else
branch returns a 0
, which the compiler infers from the return type to be of type
u32
. Since u32
is a concrete type, !
can and will be coerced to it. See issue #36375
for more information on this quirk of !
.
As it turns out, though, most traits can have an impl
for !
. Take Debug
for example:
#![feature(never_type)]
impl Debug for ! {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
*self
}
}
RunOnce again we’re using !
’s ability to coerce into any other type, in this case
fmt::Result
. Since this method takes a &!
as an argument we know that it can never be
called (because there is no value of type !
for it to be called with). Writing *self
essentially tells the compiler “We know that this code can never be run, so just treat the
entire function body as having type fmt::Result
”. This pattern can be used a lot when
implementing traits for !
. Generally, any trait which only has methods which take a self
parameter should have such an impl.
On the other hand, one trait which would not be appropriate to implement is Default
:
trait Default {
fn default() -> Self;
}
RunSince !
has no values, it has no default value either. It’s true that we could write an
impl
for this which simply panics, but the same is true for any type (we could impl Default
for (eg.) File
by just making default()
panic.)
§Never type fallback
When the compiler sees a value of type !
in a coercion site, it implicitly inserts a
coercion to allow the type checker to infer any type:
// this
let x: u8 = panic!();
// is (essentially) turned by the compiler into
let x: u8 = absurd(panic!());
// where absurd is a function with the following signature
// (it's sound, because `!` always marks unreachable code):
fn absurd<T>(_: !) -> T { ... }
RunThis can lead to compilation errors if the type cannot be inferred:
// this
{ panic!() };
// gets turned into this
{ absurd(panic!()) }; // error: can't infer the type of `absurd`
RunTo prevent such errors, the compiler remembers where it inserted absurd
calls, and
if it can’t infer the type, it uses the fallback type instead:
This is what is known as “never type fallback”.
Historically, the fallback type was ()
, causing confusing behavior where !
spontaneously
coerced to ()
, even when it would not infer ()
without the fallback. There are plans to
change it in the 2024 edition (and possibly in all editions on a later date); see
Tracking Issue for making !
fall back to !
.
Trait Implementations§
source§impl Error for !
impl Error for !
1.30.0 · source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · source§fn description(&self) -> &str
fn description(&self) -> &str
1.34.0 · source§impl From<!> for Infallible
impl From<!> for Infallible
source§fn from(x: !) -> Infallible
fn from(x: !) -> Infallible
source§impl From<!> for TryFromIntError
impl From<!> for TryFromIntError
source§fn from(never: !) -> TryFromIntError
fn from(never: !) -> TryFromIntError
source§impl Ord for !
impl Ord for !
source§impl PartialOrd for !
impl PartialOrd for !
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self
and other
) and is used by the <=
operator. Read more