DEV Community

Cover image for Rust's never type, explained
techaiwire
techaiwire

Posted on Originally published at techaiwire.com

Rust's never type, explained

Rust's never type is the type of an expression that never produces a value. It is written as a single exclamation mark, !. A function that always panics, loops forever, or returns early has this type, because it never hands a value back to the code that follows it. After ten years as an unstable feature, the never type is now stable. The pull request that did it merged on August 24, 2026, and it carries one breaking change with it.

This matters beyond compiler trivia. The never type decides what happens at the dead ends in your code, and the new fallback rule can change what a generic function infers. The Rust Reference defines ! as a type with no values, representing computations that never complete.

How it works

A type is a set of possible values. The type bool has two of them. The never type has none. RFC 1216, the 2015 proposal that started all this, calls such a type an empty type, "a type for which there is nothing of that type." Nothing of type ! can exist while a program runs, so a value of that type has no machine-level representation at all.

That emptiness gives ! its one special power. According to the Rust standard library documentation, an expression of type ! can be coerced into any other type. The compiler can promise that safely, because the coercion never has to run. If the code after a panic!() expects a String, the panic expression fits, since no String will ever be needed there. That is why return, break, continue, and panic!() can all sit in a position that expects some other type.

The standard library documents that ! implements Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd, and Error. The same documentation names one trait it should never implement: Default. Default has to produce a value, and ! cannot.

Before this stabilization, stable Rust let you write ! in only one place, as a function's return type, per the Reference. Developers who needed an empty type elsewhere wrote their own, usually enum Never {}, a pattern RFC 1216 describes. The standard library's own version is std::convert::Infallible, an enum with no variants that has been in the library since Rust 1.34.0. Its documentation describes it as the error type for errors that can never happen. A Result<T, Infallible> tells the reader the result is always Ok.

What changed and when

Three things landed together in pull request 155499, according to the PR. First, ! is stable as a full type, which is what RFC 1216 asked for in 2015. Second, Infallible becomes a type alias for !, so the two names now mean the same type. Third, and this is the breaking part, type fallback now resolves to ! in every Rust edition.

Fallback is the rule for one corner case. Sometimes the compiler reaches a spot where an expression diverges and nothing around it pins down a type. Historically it picked the unit type (), the empty tuple, in that spot. In the 2024 edition and later, the standard library documentation already describes the fallback as ! instead. The stabilization PR makes that the rule everywhere, including older editions.

The Rust project measured the change before merging it. The PR reports a crater run over 9,024 crates. Crater compiles a large sample of published crates against a proposed compiler and reports what breaks. It flagged 3,277 regressions and no fixes. The PR is milestoned for Rust 1.100.0, as Tech AI Wire's report on the stabilization noted when it merged.

One regression report is instructive. A contributor flagged that cargo-semver-checks, a tool that checks crates for breaking API changes, reported the Infallible change as breaking. The PR discussion calls that a false positive. As contributor JonathanBrouwer put it, "The type is still there, it just turned from a pub enum into a pub type."

Milestone When What happened
RFC 1216 July 19, 2015 Proposed promoting ! to a full type
Tracking issue 35121 July 29, 2016 Opened to track the RFC; PR 35162 was the first implementation
PR 65355 Rust 1.41.0 First stabilization, reverted in PR 67224 after regressions
Rust 2024 edition 2024 edition Fallback changed to ! inside the new edition first
PR 155499 August 24, 2026 ! stable, fallback universal, Infallible aliased

Why it took ten years

The tracking issue tells the story of the delay. Issue 35121 opened on July 29, 2016. A first stabilization shipped in Rust 1.41.0 and was then pulled back out, because real crates broke. The issue records the trigger: an Error conversion from Infallible stopped compiling, filed as issue 66757. The author of the final PR writes on their blog that there were five failed attempts in all. This last push took them more than two years.

The way out was staging. The tracking issue lays out a three-phase plan. Change the fallback to ! inside the 2024 edition only. Stabilize that edition. Then, once the ecosystem had absorbed the new rule, make the breaking changes universal and set Infallible equal to !. The August 2026 PR is that final phase. Rust's other big compiler change this year, the next-generation trait solver, is going through nightly first in the same cautious way, with stabilization planned for later.

What this means for developers

Check generic code on nightly before Rust 1.100.0 reaches stable. The fallback change matters where a type parameter is pinned down only by an expression that panics or returns early. In those spots the compiler used to choose () for you and now chooses !. Most code will not notice. Code that relied on the old choice can fail to compile, or pick a different trait implementation. The 3,277 crater hits show that is not hypothetical.

Grep for Infallible. If your crate implements a trait for both Infallible and !, those two implementations now collide, because they name one type. The Infallible documentation also notes that function pointer types like fn() -> ! and fn() -> Infallible could carry different trait implementations. Those now match too. A library that exposes Infallible in its public API does not break its users, whatever a semver tool says. The type is still there under both names.

Use ! on purpose from now on. A function that can only succeed can return Result<T, !>, which RFC 1216 lists as a motivating use case, and callers can match on the Ok arm alone. A handler that never returns can be passed to generic code expecting a Fn() -> T, another RFC use case, without a wrapper type. Drop the homemade enum Never {} once your minimum supported Rust version allows it.

Ten years is a long time for a one-character type. The reverted first attempt, the edition-first rollout, and the crater number explain most of it. They are also why this is a change you can prepare for on nightly today, before it reaches stable.


This article was first published on Tech AI Wire.

Also available in

Deutsch · 日本語 · Français · Español · Português

Related on Tech AI Wire

Sources

Top comments (0)