The Never Type !
The never type—written !—is Rust's built-in empty (uninhabited) type representing computations that never return control to their caller. While the unit type () has exactly 1 possible value (()), the never type ! has 0 possible values: no instance of ! can ever exist at runtime.
Cardinality Comparison: (), !, and bool
Type Cardinalities (Number of Valid Runtime Inhabitants):
`!` / `enum Infallible {}` ---> 0 inhabitants (computation diverges; never produces a value)
`()` / `struct Unit;` ---> 1 inhabitant (computation finishes with trivial `()` value)
`bool` ---> 2 inhabitants (`false`, `true`)
| Type | Inhabitants | Size in Memory (size_of) |
Meaning as a Function Return Type (fn f() -> T) |
|---|---|---|---|
! |
0 |
0 bytes |
f() never returns normally (loops forever, panics, or exits the process) |
std::convert::Infallible |
0 |
0 bytes |
Empty enum Infallible {} used in generic type positions like Result<T, Infallible> |
() |
1 (()) |
0 bytes |
f() returns normally with no meaningful payload |
Diverging Functions (fn() -> !)
A function with return type -> ! is called a diverging function. The compiler verifies that every control-flow path through a diverging function either panics, calls another diverging function (such as std::process::exit or std::hint::unreachable_unchecked), or runs an infinite loop {} without a break:
// ❌ does not compile
fn invalid_diverging(flag: bool) -> ! {
if flag {
panic!("diverged via panic");
}
// Error: if `flag` is false, control reaches the end of the function,
// producing `()` instead of diverging (`!`)!
}
fn main() {}
error[E0308]: mismatched types
--> src/main.rs:2:37
|
2 | fn invalid_diverging(flag: bool) -> ! {
| ----------------- ^ expected `!`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
Elimination of Impossible Enum Branches (Result<T, Infallible>)
When an enum variant holds ! or an uninhabited enum like std::convert::Infallible (such as Result<u32, Infallible> returned by <u32 as TryFrom<u16>>::try_from), the Err variant has 0 inhabitants (1 * 0 = 0), so the compiler knows Err is unreachable and eliminates the discriminant tag entirely:
use std::convert::Infallible;
fn always_succeeds(x: u16) -> Result<u64, Infallible> {
Ok(u64::from(x) * 10)
}
fn main() {
println!("size of u64: {} bytes", size_of::<u64>());
println!("size of Result<u64, Infallible>: {} bytes", size_of::<Result<u64, Infallible>>());
// `let Ok(val) = ...` is an irrefutable pattern when the `Err` type is uninhabited!
let Ok(val) = always_succeeds(42);
println!("unwrapped irrefutably: {}", val);
}
size of u64: 8 bytes
size of Result<u64, Infallible>: 8 bytes
unwrapped irrefutably: 420
Rust 2024 Edition Change: Never-Type Fallback to !
When a diverging expression appears in an unconstrained generic return position (such as panic!() inside a closure passed to a generic function fn call<R>(f: impl FnOnce() -> R) -> R), older Rust editions fell back to inferring R = (). Starting in the Rust 2024 edition, unconstrained never-type fallback resolves directly to R = !, and ! implements core traits (Debug, Display, Clone, Default, Error, PartialEq, Ord, Hash) in the standard library.