r[divergence]
Divergence
r[divergence.intro] A diverging expression is an expression that never completes normal execution.
#![allow(unused)] fn main() { fn diverges() -> ! { panic!("This function never returns!"); } fn example() { let x: i32 = diverges(); // This line never completes. println!("This is never printed: {x}"); } }
See the following rules for specific expression divergence behavior:
- [expr.block.diverging] — Block expressions.
- [expr.if.diverging] —
ifexpressions. - [expr.loop.block-labels.type] — Labeled block expressions with
break. - [expr.loop.break-value.diverging] —
loopexpressions withbreak. - [expr.loop.break.diverging] —
breakexpressions. - [expr.loop.continue.diverging] —
continueexpressions. - [expr.loop.infinite.diverging] — Infinite
loopexpressions. - [expr.match.diverging] —
matchexpressions. - [expr.match.empty] — Empty
matchexpressions. - [expr.return.diverging] —
returnexpressions. - [type.never.constraint] — Function calls returning
!.
[!NOTE] The [
panic!] macro and related panic-generating macros like [unreachable!] also have the type!and are diverging.
r[divergence.never]
Any expression of type ! is a diverging expression. However, diverging expressions are not limited to type !; expressions of other types may also diverge (e.g., Some(loop {}) has type Option<!>).
[!NOTE] Though
!is considered an uninhabited type, a type being uninhabited is not sufficient for it to diverge.#![allow(unused)] fn main() { enum Empty {} fn make_never() -> ! {loop{}} fn make_empty() -> Empty {loop{}} fn diverging() -> ! { // This has a type of `!`. // So, the entire function is considered diverging. make_never(); // OK: The type of the body is `!` which matches the return type. } fn not_diverging() -> ! { // This type is uninhabited. // However, the entire function is not considered diverging. make_empty(); // ERROR: The type of the body is `()` but expected type `!`. } }
[!NOTE] Divergence can propagate to the surrounding block. See [expr.block.diverging].
r[divergence.fallback]
Fallback
If a type to be inferred is only unified with diverging expressions, then that type will be inferred to be !.
[!EXAMPLE]
#![allow(unused)] fn main() { fn foo() -> i32 { 22 } match foo() { // ERROR: The trait bound `!: Default` is not satisfied. 4 => Default::default(), _ => return, }; }
[!EDITION-2024] Before the 2024 edition, the type was inferred to instead be
().
[!NOTE] Importantly, type unification may happen structurally, so the fallback
!may be part of a larger type. The following compiles:#![allow(unused)] fn main() { fn foo() -> i32 { 22 } // This has the type `Option<!>`, not `!` match foo() { 4 => Default::default(), _ => Some(return), }; }