Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/attributes/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Empty, ()> { 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]
Expand Down
4 changes: 0 additions & 4 deletions src/divergence.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
>
Expand Down
26 changes: 4 additions & 22 deletions src/expressions/block-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand All @@ -115,33 +114,16 @@ 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: !,
}

fn make<T>() -> 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
# #![ feature(never_type) ]
# fn make<T>() -> 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.
```

Expand Down
4 changes: 1 addition & 3 deletions src/expressions/match-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,9 @@ If there are no match arms, then the `match` expression is [diverging] and the t

> [!EXAMPLE]
> ```rust
> # fn make<T>() -> T { loop {} }
> enum Empty {}
>
> fn diverging_match_no_arms() -> ! {
> let e: Empty = make();
> fn diverging_match_no_arms(e: Empty) -> ! {
> match e {}
> }
> ```
Expand Down
1 change: 1 addition & 0 deletions src/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ r[glossary.zst]
A type is zero sized (a ZST) if its size is 0. Such types have at most one possible value. Examples include:

- The [unit type] (see [layout.tuple.unit]).
- The [never type] `!` (see [type.never.layout]).
- [Function items] (see [type.fn-item.intro]).
- The constructors of [tuple-like structs] (see [type.fn-item.intro]).
- The constructors of [tuple-like enum variants] (see [type.fn-item.intro]).
Expand Down
2 changes: 2 additions & 0 deletions src/type-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The size of most primitives is given in this table.
| `f32` | 4 |
| `f64` | 8 |
| `char` | 4 |
| [`!`][never-type] | 0 |

r[layout.primitive.size-minimum]
`usize` and `isize` have a size big enough to contain every address on the target platform. For example, on a 32 bit target, this is 4 bytes, and on a 64 bit target, this is 8 bytes.
Expand Down Expand Up @@ -571,3 +572,4 @@ Because this representation delegates type layout to another type, it cannot be
[structs]: items/structs.md
[`transparent`]: #the-transparent-representation
[`Layout`]: std::alloc::Layout
[never-type]: types/never.md
3 changes: 1 addition & 2 deletions src/types/closure.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
};
Expand Down
65 changes: 49 additions & 16 deletions src/types/never.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,61 @@
r[type.never]
# Never type

r[type.never.intro]
The never type `!` is a type with no values, representing computations that never complete, also known as [diverging][divergence] computations.

> [!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<u32, !> {
> Ok(42)
> }
> ```
>
> ```rust
> # use std::str::FromStr;
> struct Anything(String);
>
> impl FromStr for Anything {
> type Err = !;
>
> fn from_str(s: &str) -> Result<Self, !> {
> Ok(Anything(s.to_owned()))
> }
> }
>
> // This does not need to check for the `Err` variant because
> // `FromStr::Err` is the never type.
> let Ok(s) = Anything::from_str("example");
> ```

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.
Expressions of type `!` can be coerced into any type.

r[type.never.constraint]
The `!` type can **only** appear in function return types presently, indicating it is a diverging function that never returns.
> [!NOTE]
> The standard library type [`Infallible`] is a type alias for `!`.

```rust
fn foo() -> ! {
panic!("This call never returns.");
}
```
r[type.never.layout]
The `!` type has a size of 0 and an alignment of 1.

```rust
unsafe extern "C" {
pub safe fn no_return_extern_func() -> !;
}
```
[`Infallible`]: core::convert::Infallible
Loading