Primitive Typenever
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,theexit functionfn exit(code: i32) -> ! exits the process without ever returning, andso returns!.
break,continue andreturn expressions also have type!. For example we are allowed towrite:
Although thelet is pointless here, it illustrates the meaning of!. Sincex is neverassigned a value (becausereturn returns from the entire function),x can be given type!. We could also replacereturn 123 with apanic! or a never-endingloop and this codewould still be valid.
A more realistic usage of! is in this code:
Both match arms must produce values of typeu32, but sincebreak never produces a valueat all we know it can never produce a value which isn’t au32. This illustrates anotherbehavior 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 theFromStrtrait:
When implementing this trait forString we need to pick a type forErr. And sinceconverting 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 anErr type of!, if we have to callString::from_str for some reason the result will be aResult<String, !> which we can unpack like this:
Since theErr variant contains a!, it can never occur. This means we can exhaustivelymatch onResult<T, !> by just taking theOk variant. This illustrates another behaviorof! - it can be used to “delete” certain enum variants from generic types likeResult.
§Infinite loops
WhileResult<T, !> is very useful for removing errors,! can also be used to removesuccesses as well. If we think ofResult<T, !> as “if this function returns, it has noterrored,” we get a very intuitive idea ofResult<!, E> as well: if the function returns, ithas errored.
For example, consider the case of a simple web server, which can be simplified to:
loop{let(client, request) = get_request().expect("disconnected");letresponse = request.process(); response.send(client);}Currently, 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{matchget_request() {Err(err) =>breakerr,Ok((client, request)) => {letresponse = request.process(); response.send(client); }, }}Now, when the server disconnects, we exit the loop with an error instead of panicking. While itmight be intuitive to simply return the error, we might want to wrap it in aResult<!, E>instead:
fnserver_loop() ->Result<!, ConnectionError> {loop{let(client, request) = get_request()?;letresponse = request.process(); response.send(client); }}Now, we can use? instead ofmatch, and the return type makes a lot more sense: if the loopever stops, it means that an error occurred. We don’t even have to wrap the loop in anOkbecause! coerces toResult<!, ConnectionError> automatically.
§! and traits
When writing your own traits,! should have animpl whenever there is an obviousimplwhich doesn’tpanic!. The reason is that functions returning animpl Trait where!does not have animpl ofTrait cannot diverge as their only possible code path. In otherwords, they can’t return! from every code path. As an example, this code doesn’t compile:
But this code does:
The reason is that, in the first example, there are many possible types that! could coerceto, because many types implementAdd<u32>. However, in the second example,theelse branch returns a0, which the compiler infers from the return type to be of typeu32. Sinceu32 is a concrete type,! can and will be coerced to it. See issue#36375for more information on this quirk of!.
As it turns out, though, most traits can have animpl for!. TakeDebugfor example:
#![feature(never_type)]implDebugfor ! {fnfmt(&self, formatter:&mutfmt::Formatter<'_>) -> fmt::Result {*self}}Once again we’re using!’s ability to coerce into any other type, in this casefmt::Result. Since this method takes a&! as an argument we know that it can never becalled (because there is no value of type! for it to be called with). Writing*selfessentially tells the compiler “We know that this code can never be run, so just treat theentire function body as having typefmt::Result”. This pattern can be used a lot whenimplementing traits for!. Generally, any trait which only has methods which take aselfparameter should have such an impl.
On the other hand, one trait which would not be appropriate to implement isDefault:
Since! has no values, it has no default value either. It’s true that we could write animpl for this which simply panics, but the same is true for any type (we couldimpl Default for (eg.)File by just makingdefault() panic.)
§Never type fallback
When the compiler sees a value of type! in acoercion site, it implicitly inserts acoercion to allow the type checker to infer any type:
// thisletx: u8 =panic!();// is (essentially) turned by the compiler intoletx: u8 = absurd(panic!());// where absurd is a function with the following signature// (it's sound, because `!` always marks unreachable code):fnabsurd<T>(_: !) -> T { ... }This 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`To prevent such errors, the compiler remembers where it insertedabsurd calls, andif 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! spontaneouslycoerced to(), even when it would not infer() without the fallback. The fallback was changedto! in the2024 edition, and will be changed in all editions at a later date.