Work in progress
Divergence
If an expression diverges, then nothing after that expression will execute. Importantly, while there are certain language constructs that immediately produce a diverging expression of the type
!, divergence can also propogate to the surrounding block — where divergence indicates that the block itself will never finish executing.Any expression of type
!is a diverging expression, but there are also diverging expressions which are not of type!(e.g.Some(loop {})produces a type ofOption<!>).Note
Though
!is considered an uninhabited type, a type being uninhabited is not sufficient for it to diverge.#![allow(unused)] fn main() { #![ feature(never_type) ] fn make<T>() -> T { loop {} } enum Empty {} fn diverging() -> ! { // This has a type of `!`. // So, the entire function is considered diverging make::<!>(); } fn not_diverging() -> ! { // This type is uninhabited. // However, the entire function is not considered diverging make::<Empty>(); } }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, }; }2024 Edition differences
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), }; }