Rust's
!(the never type) was stabilized on nightly on 2026-08-24 and ships on stable in Rust 1.100, scheduled for 2026-11-12. This is not a "new feature" tour. It takes an implementer's view of why!stayed unstable for ten years: what the inference rule called never type fallback actually does, and why switching it from()to!became a breaking change for every edition.
! had been unstable since its 2016 tracking issue (rust-lang/rust#35121). The stabilization PR (#155499, by WaffleLapkin) merged on 2026-08-24. The author's blog post sums it up: ten years unstable, five failed stabilization attempts, and more than two years of their own work.!. It bundles three semantic changes: (1) never type fallback becomes ! on all editions, (2) std::convert::Infallible becomes a type alias for !, and (3) the migration lint dependency_on_unit_never_type_fallback is removed.Infallible is now a type alias to !" and "The never type fallback was changed to ! on all editions" under Compatibility Notes. So crates on edition 2021 or older can compile differently starting mid-November.Allocator. Before you bump the toolchain in CI, it's worth knowing which code will fail to compile and which code could quietly change meaning.! plays two rolesreturn, break, continue, panic!(), loop {} (with no break), std::process::exit and the like produce no value, so their type is !.Err of a Result<T, !>, so you can destructure it with an irrefutable pattern like let Ok(x) = r;.Never-to-any coercion is what makes both roles work at once. ! implicitly converts to any type. Since no value can exist, the conversion code never runs, so this is sound.
let value = match option {
Some(x) => x, // u32
None => return, // ! coerced to u32
};
At sites where coercion can happen, the compiler conceptually inserts this function (as the Edition Guide puts it):
fn absurd<T>(x: !) -> T { x }
let x: u8 = panic!();
// ≈ let x: u8 = absurd(panic!());
That's fine when the surroundings determine T. The trouble starts when nothing does, for example because the value is discarded:
{ panic!() }; // ≈ { absurd::<?>(panic!()) }; ← ? is undetermined
Making every such case an error would break perfectly ordinary code like if c { foo() } else { return };. So rustc has a rule: if the type variable at an absurd insertion point is still undetermined at the end, plug in a designated type, the fallback type. That is never type fallback.
Historically the fallback type was (), a holdover from when ! wasn't a first-class type. As a result, values of type ! would spontaneously "decay" into (). The Edition Guide says plainly that ! spontaneously coercing to () even when inference had no reason to pick () was confusing and blocked stabilization of !.