From cadf52238686f7a19674910856f4098097def694 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 22 Apr 2026 05:54:03 +0000 Subject: [PATCH 1/7] Document stabilization of the never type Stabilizing the never type means making `!` available in all positions and changing the never-to-any coercion fallback type to `!` in older editions. Let's make the updates needed for that. --- src/divergence.md | 4 ---- src/expressions/block-expr.md | 6 ------ src/types/never.md | 20 ++++++++++++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/divergence.md b/src/divergence.md index 5e25d6d0d9..ab6ba0ab8b 100644 --- a/src/divergence.md +++ b/src/divergence.md @@ -27,7 +27,6 @@ See the following rules for specific expression divergence behavior: - [expr.match.diverging] --- `match` expressions. - [expr.match.empty] --- Empty `match` expressions. - [expr.return.diverging] --- `return` expressions. -- [type.never.constraint] --- Function calls returning `!`. > [!NOTE] > The [`panic!`] macro and related panic-generating macros like [`unreachable!`] also have the type [`!`] and are diverging. @@ -75,9 +74,6 @@ If a type to be inferred is only unified with diverging expressions, then that t > }; > ``` -> [!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: > diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index 52c3050619..e2c42c07dd 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -91,7 +91,6 @@ r[expr.block.diverging] A block is considered to be [diverging][divergence] if all reachable control flow paths contain a diverging expression, unless that expression is a [place expression] that is not read from. ```rust,no_run -# #![ feature(never_type) ] fn no_control_flow() -> ! { // There are no conditional statements, so this entire function body is diverging. loop {} @@ -115,10 +114,6 @@ fn control_flow_not_diverging() -> () { } } -// Note: This makes use of the unstable never type which is only available on -// Rust's nightly channel. This is done for illustration purposes. It is -// possible to encounter this scenario in stable Rust, but requires a more -// convoluted example. struct Foo { x: !, } @@ -133,7 +128,6 @@ fn diverging_place_read() -> ! { ``` ```rust,compile_fail,E0308 -# #![ feature(never_type) ] # fn make() -> T { loop {} } # struct Foo { # x: !, diff --git a/src/types/never.md b/src/types/never.md index fe30bce74d..e32a6838a8 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -12,12 +12,9 @@ The never type `!` is a type with no values, representing the result of computat r[type.never.coercion] Expressions of type `!` can be coerced into any other type. -r[type.never.constraint] -The `!` type can **only** appear in function return types presently, indicating it is a diverging function that never returns. - ```rust fn foo() -> ! { - panic!("This call never returns."); + loop {} } ``` @@ -26,3 +23,18 @@ unsafe extern "C" { pub safe fn no_return_extern_func() -> !; } ``` + +```rust,no_run +let _: ! = loop {}; +``` + +```rust +fn always_ok() -> Result { + Ok(42) +} +``` + +> [!NOTE] +> The standard library type [`Infallible`] is a type alias for `!`. + +[`Infallible`]: core::convert::Infallible From 16f507d28afcc3ca1a024d4da939738fd8f637da Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Wed, 22 Apr 2026 05:55:25 +0000 Subject: [PATCH 2/7] Simplify examples that worked around unstable `!` In various places, we had worked around the never type being unstable when writing examples. Now that it is stable, let's clean those examples up. --- src/attributes/diagnostics.md | 9 ++++----- src/expressions/block-expr.md | 20 ++++---------------- src/expressions/match-expr.md | 4 +--- src/types/closure.md | 3 +-- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/attributes/diagnostics.md b/src/attributes/diagnostics.md index d1d1b65ddc..b079d95622 100644 --- a/src/attributes/diagnostics.md +++ b/src/attributes/diagnostics.md @@ -401,11 +401,10 @@ As an exception to [attributes.diagnostics.must_use.type], the lint does not fir ```rust #![deny(unused_must_use)] # use core::ops::ControlFlow; -enum Empty {} -fn f1() -> Result<(), Empty> { Ok(()) } -f1(); // OK: `Empty` is uninhabited. -fn f2() -> ControlFlow { ControlFlow::Continue(()) } -f2(); // OK: `Empty` is uninhabited. +fn f1() -> Result<(), !> { Ok(()) } +f1(); // OK: `!` is uninhabited. +fn f2() -> ControlFlow { ControlFlow::Continue(()) } +f2(); // OK: `!` is uninhabited. ``` r[attributes.diagnostics.must_use.fn] diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index e2c42c07dd..8d0cf077dd 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -114,28 +114,16 @@ fn control_flow_not_diverging() -> () { } } -struct Foo { - x: !, -} - -fn make() -> T { loop {} } - -fn diverging_place_read() -> ! { - let foo = Foo { x: make() }; +fn diverging_place_read(x: !) -> ! { // A read of a place expression produces a diverging block. - let _x = foo.x; + let _x = x; } ``` ```rust,compile_fail,E0308 -# fn make() -> T { loop {} } -# struct Foo { -# x: !, -# } -fn diverging_place_not_read() -> ! { - let foo = Foo { x: make() }; +fn diverging_place_not_read(x: !) -> ! { // Assignment to `_` means the place is not read. - let _ = foo.x; + let _ = x; } // ERROR: Mismatched types. ``` diff --git a/src/expressions/match-expr.md b/src/expressions/match-expr.md index 28d1e6351c..2cbe38eab6 100644 --- a/src/expressions/match-expr.md +++ b/src/expressions/match-expr.md @@ -119,11 +119,9 @@ If there are no match arms, then the `match` expression is [diverging] and the t > [!EXAMPLE] > ```rust -> # fn make() -> T { loop {} } > enum Empty {} > -> fn diverging_match_no_arms() -> ! { -> let e: Empty = make(); +> fn diverging_match_no_arms(e: Empty) -> ! { > match e {} > } > ``` diff --git a/src/types/closure.md b/src/types/closure.md index 578e29faf9..94e4ee031b 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -340,8 +340,7 @@ r[type.closure.capture.precision.discriminants.uninhabited-variants] Even if all variants but the one being matched against are uninhabited, making the pattern [irrefutable][patterns.refutable], the discriminant is still read if it otherwise would be. ```rust,compile_fail,E0502 -enum Empty {} -let mut x = Ok::<_, Empty>(42); +let mut x = Ok::<_, !>(42); let c = || { let Ok(_) = x; // Captures `x` by `ImmBorrow`. }; From b951325c572dc084f0215f6d262eabccb37679ad Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 12 Jun 2026 09:12:49 -0700 Subject: [PATCH 3/7] Move type.never.intro to the top This moves type.never.intro to the top of the chapter to match the style where the intro should always come first in a section. --- src/types/never.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/never.md b/src/types/never.md index e32a6838a8..4426b08fbd 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -1,14 +1,14 @@ r[type.never] # Never type +r[type.never.intro] +The never type `!` is a type with no values, representing the result of computations that never complete. + r[type.never.syntax] ```grammar,types NeverType -> `!` ``` -r[type.never.intro] -The never type `!` is a type with no values, representing the result of computations that never complete. - r[type.never.coercion] Expressions of type `!` can be coerced into any other type. From f8b82e5866016a429a44ce9be560d44fadf5d916 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 12 Jun 2026 09:41:32 -0700 Subject: [PATCH 4/7] Move never type example, and update for stabilization This moves the never example to the intro to match our layout style, and adds an example showing a type expression using `!`. --- src/types/never.md | 60 +++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/types/never.md b/src/types/never.md index 4426b08fbd..0fd5fd7b72 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -4,6 +4,44 @@ r[type.never] r[type.never.intro] The never type `!` is a type with no values, representing the result of computations that never complete. +> [!EXAMPLE] +> ```rust +> fn foo() -> ! { +> loop {} +> } +> ``` +> +> ```rust +> unsafe extern "C" { +> pub safe fn no_return_extern_func() -> !; +> } +> ``` +> +> ```rust,no_run +> let _: ! = loop {}; +> ``` +> +> ```rust +> fn always_ok() -> Result { +> Ok(42) +> } +> ``` +> +> ```rust +> # use std::str::FromStr; +> struct Anything(String); +> +> impl FromStr for Anything { +> type Err = !; +> +> fn from_str(s: &str) -> Result { +> Ok(Anything(s.to_owned())) +> } +> } +> +> let Ok(s) = Anything::from_str("example"); +> ``` + r[type.never.syntax] ```grammar,types NeverType -> `!` @@ -12,28 +50,6 @@ NeverType -> `!` r[type.never.coercion] Expressions of type `!` can be coerced into any other type. -```rust -fn foo() -> ! { - loop {} -} -``` - -```rust -unsafe extern "C" { - pub safe fn no_return_extern_func() -> !; -} -``` - -```rust,no_run -let _: ! = loop {}; -``` - -```rust -fn always_ok() -> Result { - Ok(42) -} -``` - > [!NOTE] > The standard library type [`Infallible`] is a type alias for `!`. From 1d1ef0a882039911c0852b8950cc2c0bf58e8572 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 15 Jun 2026 12:30:35 -0700 Subject: [PATCH 5/7] Remove "other" on never type coercion Never can coerce to itself. --- src/types/never.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/never.md b/src/types/never.md index 0fd5fd7b72..e093ef14cd 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -48,7 +48,7 @@ NeverType -> `!` ``` r[type.never.coercion] -Expressions of type `!` can be coerced into any other type. +Expressions of type `!` can be coerced into any type. > [!NOTE] > The standard library type [`Infallible`] is a type alias for `!`. From 8eb18180aac533e0adae3e9c141a1b480f595c3d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 15 Jun 2026 12:46:36 -0700 Subject: [PATCH 6/7] Link to divergence in never type intro This tries to punch up the never type intro by linking to the divergence chapter. --- src/types/never.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/never.md b/src/types/never.md index e093ef14cd..b0f039c7a9 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -2,7 +2,7 @@ r[type.never] # Never type r[type.never.intro] -The never type `!` is a type with no values, representing the result of computations that never complete. +The never type `!` is a type with no values, representing computations that never complete, also known as [diverging][divergence] computations. > [!EXAMPLE] > ```rust From bfba2556b6d950b09ce8d14bf6a3903781d8c0b0 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 23 Jun 2026 11:55:19 -0700 Subject: [PATCH 7/7] Add a comment explaining the FromStr example --- src/types/never.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/never.md b/src/types/never.md index b0f039c7a9..414862fe52 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -39,6 +39,8 @@ The never type `!` is a type with no values, representing computations that neve > } > } > +> // This does not need to check for the `Err` variant because +> // `FromStr::Err` is the never type. > let Ok(s) = Anything::from_str("example"); > ```