From fc0f4e5e2fb48266cde7e2580a9edcf54e9e75f6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:27:26 -0700 Subject: [PATCH 01/17] Move non_exhaustive examples into example blocks --- src/attributes/type_system.md | 319 ++++++++++++++++++---------------- 1 file changed, 165 insertions(+), 154 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 953353c448..8d15748466 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -10,6 +10,61 @@ r[attributes.type-system.non_exhaustive.intro] The *`non_exhaustive` attribute* indicates that a type or variant may have more fields or variants added in the future. +> [!EXAMPLE] +> The following non-exhaustive definitions will be used in the examples that follow. +> +> ```rust +> #[non_exhaustive] +> pub struct Config { +> pub window_width: u16, +> pub window_height: u16, +> } +> +> #[non_exhaustive] +> pub struct Token; +> +> #[non_exhaustive] +> pub struct Id(pub u64); +> +> #[non_exhaustive] +> pub enum Error { +> Message(String), +> Other, +> } +> +> pub enum Message { +> #[non_exhaustive] Send { from: u32, to: u32, contents: String }, +> #[non_exhaustive] Reaction(u32), +> #[non_exhaustive] Quit, +> } +> +> // Non-exhaustive structs can be constructed as normal within the defining crate. +> let config = Config { window_width: 640, window_height: 480 }; +> let token = Token; +> let id = Id(4); +> +> // Non-exhaustive structs can be matched on exhaustively within the defining crate. +> let Config { window_width, window_height } = config; +> let Token = token; +> let Id(id_number) = id; +> +> let error = Error::Other; +> let message = Message::Reaction(3); +> +> // Non-exhaustive enums can be matched on exhaustively within the defining crate. +> match error { +> Error::Message(ref s) => { }, +> Error::Other => { }, +> } +> +> match message { +> // Non-exhaustive variants can be matched on exhaustively within the defining crate. +> Message::Send { from, to, contents } => { }, +> Message::Reaction(id) => { }, +> Message::Quit => { }, +> } +> ``` + r[attributes.type-system.non_exhaustive.allowed-positions] It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. @@ -20,58 +75,6 @@ take any inputs. r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect. -```rust -#[non_exhaustive] -pub struct Config { - pub window_width: u16, - pub window_height: u16, -} - -#[non_exhaustive] -pub struct Token; - -#[non_exhaustive] -pub struct Id(pub u64); - -#[non_exhaustive] -pub enum Error { - Message(String), - Other, -} - -pub enum Message { - #[non_exhaustive] Send { from: u32, to: u32, contents: String }, - #[non_exhaustive] Reaction(u32), - #[non_exhaustive] Quit, -} - -// Non-exhaustive structs can be constructed as normal within the defining crate. -let config = Config { window_width: 640, window_height: 480 }; -let token = Token; -let id = Id(4); - -// Non-exhaustive structs can be matched on exhaustively within the defining crate. -let Config { window_width, window_height } = config; -let Token = token; -let Id(id_number) = id; - -let error = Error::Other; -let message = Message::Reaction(3); - -// Non-exhaustive enums can be matched on exhaustively within the defining crate. -match error { - Error::Message(ref s) => { }, - Error::Other => { }, -} - -match message { - // Non-exhaustive variants can be matched on exhaustively within the defining crate. - Message::Send { from, to, contents } => { }, - Message::Reaction(id) => { }, - Message::Quit => { }, -} -``` - r[attributes.type-system.non_exhaustive.external-crate] Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. @@ -89,115 +92,123 @@ Non-exhaustive types cannot be constructed outside of the defining crate: (as is the case without `#[non_exhaustive]`). - [`enum`][enum] instances can be constructed. -The following examples of construction do not compile when outside the defining crate: - - -```rust,ignore -// These are types defined in an upstream crate that have been annotated as -// `#[non_exhaustive]`. -use upstream::{Config, Token, Id, Error, Message}; - -// Cannot construct an instance of `Config`; if new fields were added in -// a new version of `upstream` then this would fail to compile, so it is -// disallowed. -let config = Config { window_width: 640, window_height: 480 }; - -// Cannot construct an instance of `Token`; if new fields were added, then -// it would not be a unit-like struct any more, so the same-named constant -// created by it being a unit-like struct is not public outside the crate; -// this code fails to compile. -let token = Token; - -// Cannot construct an instance of `Id`; if new fields were added, then -// its constructor function signature would change, so its constructor -// function is not public outside the crate; this code fails to compile. -let id = Id(5); - -// Can construct an instance of `Error`; new variants being introduced would -// not result in this failing to compile. -let error = Error::Message("foo".to_string()); - -// Cannot construct an instance of `Message::Send` or `Message::Reaction`; -// if new fields were added in a new version of `upstream` then this would -// fail to compile, so it is disallowed. -let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; -let message = Message::Reaction(0); - -// Cannot construct an instance of `Message::Quit`; if this were converted to -// a tuple enum variant `upstream`, this would fail to compile. -let message = Message::Quit; -``` +> [!EXAMPLE] +> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of construction do not compile when outside the defining crate: +> +> +> ```rust,ignore +> // These are types defined in an upstream crate that have been annotated as +> // `#[non_exhaustive]`. +> use upstream::{Config, Token, Id, Error, Message}; +> +> // Cannot construct an instance of `Config`; if new fields were added in +> // a new version of `upstream` then this would fail to compile, so it is +> // disallowed. +> let config = Config { window_width: 640, window_height: 480 }; +> +> // Cannot construct an instance of `Token`; if new fields were added, then +> // it would not be a unit-like struct any more, so the same-named constant +> // created by it being a unit-like struct is not public outside the crate; +> // this code fails to compile. +> let token = Token; +> +> // Cannot construct an instance of `Id`; if new fields were added, then +> // its constructor function signature would change, so its constructor +> // function is not public outside the crate; this code fails to compile. +> let id = Id(5); +> +> // Can construct an instance of `Error`; new variants being introduced would +> // not result in this failing to compile. +> let error = Error::Message("foo".to_string()); +> +> // Cannot construct an instance of `Message::Send` or `Message::Reaction`; +> // if new fields were added in a new version of `upstream` then this would +> // fail to compile, so it is disallowed. +> let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; +> let message = Message::Reaction(0); +> +> // Cannot construct an instance of `Message::Quit`; if this were converted to +> // a tuple enum variant `upstream`, this would fail to compile. +> let message = Message::Quit; +> ``` r[attributes.type-system.non_exhaustive.match] There are limitations when matching on non-exhaustive types outside of the defining crate: -- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's [visibility] is reduced to be no greater than `pub(crate)`. -- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not contribute towards the exhaustiveness of the arms. The following examples of matching do not compile when outside the defining crate: - - -```rust, ignore -// These are types defined in an upstream crate that have been annotated as -// `#[non_exhaustive]`. -use upstream::{Config, Token, Id, Error, Message}; - -// Cannot match on a non-exhaustive enum without including a wildcard arm. -match error { - Error::Message(ref s) => { }, - Error::Other => { }, - // would compile with: `_ => {},` -} - -// Cannot match on a non-exhaustive struct without a wildcard. -if let Ok(Config { window_width, window_height }) = config { - // would compile with: `..` -} - -// Cannot match a non-exhaustive unit-like or tuple struct except by using -// braced struct syntax with a wildcard. -// This would compile as `let Token { .. } = token;` -let Token = token; -// This would compile as `let Id { 0: id_number, .. } = id;` -let Id(id_number) = id; - -match message { - // Cannot match on a non-exhaustive struct enum variant without including a wildcard. - Message::Send { from, to, contents } => { }, - // Cannot match on a non-exhaustive tuple or unit enum variant. - Message::Reaction(type) => { }, - Message::Quit => { }, -} -``` +- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), + a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's + [visibility] is reduced to be no greater than `pub(crate)`. +- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not + contribute towards the exhaustiveness of the arms. + +> [!EXAMPLE] +> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of matching do not compile when outside the defining crate: +> +> +> ```rust, ignore +> // These are types defined in an upstream crate that have been annotated as +> // `#[non_exhaustive]`. +> use upstream::{Config, Token, Id, Error, Message}; +> +> // Cannot match on a non-exhaustive enum without including a wildcard arm. +> match error { +> Error::Message(ref s) => { }, +> Error::Other => { }, +> // would compile with: `_ => {},` +> } +> +> // Cannot match on a non-exhaustive struct without a wildcard. +> if let Ok(Config { window_width, window_height }) = config { +> // would compile with: `..` +> } +> +> // Cannot match a non-exhaustive unit-like or tuple struct except by using +> // braced struct syntax with a wildcard. +> // This would compile as `let Token { .. } = token;` +> let Token = token; +> // This would compile as `let Id { 0: id_number, .. } = id;` +> let Id(id_number) = id; +> +> match message { +> // Cannot match on a non-exhaustive struct enum variant without including a wildcard. +> Message::Send { from, to, contents } => { }, +> // Cannot match on a non-exhaustive tuple or unit enum variant. +> Message::Reaction(type) => { }, +> Message::Quit => { }, +> } +> ``` It's also not allowed to use numeric casts (`as`) on enums that contain any non-exhaustive variants. -For example, the following enum can be cast because it doesn't contain any non-exhaustive variants: - -```rust -#[non_exhaustive] -pub enum Example { - First, - Second -} -``` - -However, if the enum contains even a single non-exhaustive variant, casting will result in an error. Consider this modified version of the same enum: - -```rust -#[non_exhaustive] -pub enum EnumWithNonExhaustiveVariants { - First, - #[non_exhaustive] - Second -} -``` - - -```rust,ignore -use othercrate::EnumWithNonExhaustiveVariants; - -// Error: cannot cast an enum with a non-exhaustive variant when it's defined in another crate -let _ = EnumWithNonExhaustiveVariants::First as u8; -``` +> [!EXAMPLE] +> The following enum can be cast because it doesn't contain any non-exhaustive variants: +> +> ```rust +> #[non_exhaustive] +> pub enum Example { +> First, +> Second +> } +> ``` +> +> However, if the enum contains even a single non-exhaustive variant, casting will result in an error. Consider this modified version of the same enum: +> +> ```rust +> #[non_exhaustive] +> pub enum EnumWithNonExhaustiveVariants { +> First, +> #[non_exhaustive] +> Second +> } +> ``` +> +> +> ```rust,ignore +> use othercrate::EnumWithNonExhaustiveVariants; +> +> // Error: cannot cast an enum with a non-exhaustive variant when it's defined in another crate +> let _ = EnumWithNonExhaustiveVariants::First as u8; +> ``` Non-exhaustive types are always considered inhabited in downstream crates. From 011cbb699e82d2a4936620df56a9e160fe167289 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:30:38 -0700 Subject: [PATCH 02/17] Move the same-crate example into the same-crate rule --- src/attributes/type_system.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 8d15748466..7d934ea1fd 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -37,7 +37,22 @@ more fields or variants added in the future. > #[non_exhaustive] Reaction(u32), > #[non_exhaustive] Quit, > } +> ``` + +r[attributes.type-system.non_exhaustive.allowed-positions] +It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. + +r[attributes.type-system.non_exhaustive.syntax] +The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not +take any inputs. + +r[attributes.type-system.non_exhaustive.same-crate] +Within the defining crate, `non_exhaustive` has no effect. + +> [!EXAMPLE] +> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples when used within the same crate are allowed without restrictions. > +> ```rust,ignore > // Non-exhaustive structs can be constructed as normal within the defining crate. > let config = Config { window_width: 640, window_height: 480 }; > let token = Token; @@ -65,15 +80,6 @@ more fields or variants added in the future. > } > ``` -r[attributes.type-system.non_exhaustive.allowed-positions] -It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. - -r[attributes.type-system.non_exhaustive.syntax] -The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not -take any inputs. - -r[attributes.type-system.non_exhaustive.same-crate] -Within the defining crate, `non_exhaustive` has no effect. r[attributes.type-system.non_exhaustive.external-crate] Outside of the defining crate, types annotated with `non_exhaustive` have limitations that From af2345e78b90929424a68bfaf8e3116734169b99 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:32:15 -0700 Subject: [PATCH 03/17] Unwrap all lines in the non_exhaustive sections --- src/attributes/type_system.md | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 7d934ea1fd..489bf8767a 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -7,8 +7,7 @@ r[attributes.type-system.non_exhaustive] ## The `non_exhaustive` attribute r[attributes.type-system.non_exhaustive.intro] -The *`non_exhaustive` attribute* indicates that a type or variant may have -more fields or variants added in the future. +The *`non_exhaustive` attribute* indicates that a type or variant may have more fields or variants added in the future. > [!EXAMPLE] > The following non-exhaustive definitions will be used in the examples that follow. @@ -43,8 +42,7 @@ r[attributes.type-system.non_exhaustive.allowed-positions] It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. r[attributes.type-system.non_exhaustive.syntax] -The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not -take any inputs. +The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not take any inputs. r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect. @@ -82,20 +80,13 @@ Within the defining crate, `non_exhaustive` has no effect. r[attributes.type-system.non_exhaustive.external-crate] -Outside of the defining crate, types annotated with `non_exhaustive` have limitations that -preserve backwards compatibility when new fields or variants are added. +Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. r[attributes.type-system.non_exhaustive.construction] Non-exhaustive types cannot be constructed outside of the defining crate: -- Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed - with a [StructExpression] \(including with [functional update syntax]). -- The implicitly defined same-named constant of a [unit-like struct][struct], - or the same-named constructor function of a [tuple struct][struct], - has a [visibility] no greater than `pub(crate)`. - That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility - is `pub(crate)`, and otherwise the visibility of the two items is the same - (as is the case without `#[non_exhaustive]`). +- Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed with a [StructExpression] \(including with [functional update syntax]). +- The implicitly defined same-named constant of a [unit-like struct][struct], or the same-named constructor function of a [tuple struct][struct], has a [visibility] no greater than `pub(crate)`. That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility is `pub(crate)`, and otherwise the visibility of the two items is the same (as is the case without `#[non_exhaustive]`). - [`enum`][enum] instances can be constructed. > [!EXAMPLE] @@ -141,11 +132,8 @@ Non-exhaustive types cannot be constructed outside of the defining crate: r[attributes.type-system.non_exhaustive.match] There are limitations when matching on non-exhaustive types outside of the defining crate: -- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), - a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's - [visibility] is reduced to be no greater than `pub(crate)`. -- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not - contribute towards the exhaustiveness of the arms. +- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's [visibility] is reduced to be no greater than `pub(crate)`. +- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not contribute towards the exhaustiveness of the arms. > [!EXAMPLE] > Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of matching do not compile when outside the defining crate: From f37eb618f50b337c8d55fb07584c00a2016c760d Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:33:41 -0700 Subject: [PATCH 04/17] Add missing inhabited rule name, and clarify This adds a rule name that was missing for this statement. It also clarifies why this is being said. --- src/attributes/type_system.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 489bf8767a..e8b7cf3aea 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -204,7 +204,8 @@ It's also not allowed to use numeric casts (`as`) on enums that contain any non- > let _ = EnumWithNonExhaustiveVariants::First as u8; > ``` -Non-exhaustive types are always considered inhabited in downstream crates. +r[attributes.type-system.non_exhaustive.inhabited] +Non-exhaustive types are always considered [inhabited] in downstream crates, even though the constructors are not accessible. [`match`]: ../expressions/match-expr.md [attributes]: ../attributes.md @@ -212,3 +213,4 @@ Non-exhaustive types are always considered inhabited in downstream crates. [functional update syntax]: ../expressions/struct-expr.md#functional-update-syntax [struct]: ../items/structs.md [visibility]: ../visibility-and-privacy.md +[inhabited]: ../glossary.md#inhabited From f10e8eba5a7d276d6f357c60391eb6c9587e0d72 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:36:29 -0700 Subject: [PATCH 05/17] Remove attributes.type-system.non_exhaustive.external-crate This statement isn't really a rule, but more of a high-level explanation. I have moved it to the intro where I think it better belongs. --- src/attributes/type_system.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index e8b7cf3aea..69964b658e 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -7,7 +7,7 @@ r[attributes.type-system.non_exhaustive] ## The `non_exhaustive` attribute r[attributes.type-system.non_exhaustive.intro] -The *`non_exhaustive` attribute* indicates that a type or variant may have more fields or variants added in the future. +The *`non_exhaustive` attribute* indicates that a type or variant may have more fields or variants added in the future. Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. > [!EXAMPLE] > The following non-exhaustive definitions will be used in the examples that follow. @@ -78,10 +78,6 @@ Within the defining crate, `non_exhaustive` has no effect. > } > ``` - -r[attributes.type-system.non_exhaustive.external-crate] -Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. - r[attributes.type-system.non_exhaustive.construction] Non-exhaustive types cannot be constructed outside of the defining crate: From d3e48d1882b7a700a6fd9c076a59480a81a05970 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:37:31 -0700 Subject: [PATCH 06/17] Move syntax to match the order of the template --- src/attributes/type_system.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 69964b658e..dc18dafda2 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -38,12 +38,12 @@ The *`non_exhaustive` attribute* indicates that a type or variant may have more > } > ``` -r[attributes.type-system.non_exhaustive.allowed-positions] -It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. - r[attributes.type-system.non_exhaustive.syntax] The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not take any inputs. +r[attributes.type-system.non_exhaustive.allowed-positions] +It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. + r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect. From 81577d3db185b90360c5f4e17e0009d4d88f08e5 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:38:28 -0700 Subject: [PATCH 07/17] Update allowed-positions Some word cleanup and a hidden clarification. --- src/attributes/type_system.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index dc18dafda2..f7a11d4d1d 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -42,7 +42,10 @@ r[attributes.type-system.non_exhaustive.syntax] The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not take any inputs. r[attributes.type-system.non_exhaustive.allowed-positions] -It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. +The `non_exhaustive` attribute may only be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. + +> [!NOTE] +> `rustc` currently warns in some other positions, but this may be rejected in the future. r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect. From e69fe00ec531bcc3dd5a5bc3b1b8d94be5684ff9 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:38:53 -0700 Subject: [PATCH 08/17] Add attributes.type-system.non_exhaustive.duplicates --- src/attributes/type_system.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index f7a11d4d1d..feda172d32 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -47,6 +47,12 @@ The `non_exhaustive` attribute may only be applied to [`struct`s][struct], [`enu > [!NOTE] > `rustc` currently warns in some other positions, but this may be rejected in the future. +r[attributes.type-system.non_exhaustive.duplicates] +Duplicate instances of the `non_exhaustive` attribute have no effect. + +> [!NOTE] +> `rustc` warns on subsequent duplicate `non_exhaustive` attributes. + r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect. From 7b8baa60f28e77fc1f2010c195c5cc6b878349b7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:46:53 -0700 Subject: [PATCH 09/17] Reword attributes.type-system.non_exhaustive.construction The original felt a little awkward with how it differentiated between enum and enum variants. I also wasn't terribly happy with using a list here. --- src/attributes/type_system.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index feda172d32..7a65657228 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -88,11 +88,11 @@ Within the defining crate, `non_exhaustive` has no effect. > ``` r[attributes.type-system.non_exhaustive.construction] -Non-exhaustive types cannot be constructed outside of the defining crate: +`non_exhaustive` applied to a [`struct`][struct] or [`enum` variant][enum] prevents constructing that struct or enum variant outside of the defining crate. -- Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed with a [StructExpression] \(including with [functional update syntax]). -- The implicitly defined same-named constant of a [unit-like struct][struct], or the same-named constructor function of a [tuple struct][struct], has a [visibility] no greater than `pub(crate)`. That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility is `pub(crate)`, and otherwise the visibility of the two items is the same (as is the case without `#[non_exhaustive]`). -- [`enum`][enum] instances can be constructed. +Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed with a [StructExpression] \(including with [functional update syntax]). + +The implicitly defined same-named constant of a [unit-like struct][struct], or the same-named constructor function of a [tuple struct][struct], has a [visibility] no greater than `pub(crate)`. That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility is `pub(crate)`, and otherwise the visibility of the two items is the same (as is the case without `#[non_exhaustive]`). > [!EXAMPLE] > Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of construction do not compile when outside the defining crate: From eeb03f8683723daade13be9d1d2220ce82f6cd51 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:48:47 -0700 Subject: [PATCH 10/17] Remove unused link definition --- src/attributes/type_system.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 7a65657228..1ea97d651f 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -212,7 +212,6 @@ It's also not allowed to use numeric casts (`as`) on enums that contain any non- r[attributes.type-system.non_exhaustive.inhabited] Non-exhaustive types are always considered [inhabited] in downstream crates, even though the constructors are not accessible. -[`match`]: ../expressions/match-expr.md [attributes]: ../attributes.md [enum]: ../items/enumerations.md [functional update syntax]: ../expressions/struct-expr.md#functional-update-syntax From f50c66b0311b85acf2101facd28d736c1d137447 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:49:47 -0700 Subject: [PATCH 11/17] Add missing rule for non_exhaustive enum cast This adds a missing rule for this paragraph. It also adds a little more clarity as to what it means. --- src/attributes/type_system.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 1ea97d651f..15a56e14f4 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -177,7 +177,8 @@ There are limitations when matching on non-exhaustive types outside of the defin > } > ``` -It's also not allowed to use numeric casts (`as`) on enums that contain any non-exhaustive variants. +r[attributes.type-system.non_exhaustive.enum-cast] +It is not allowed to use an [`as` numeric cast][expr.as] to [access the discriminant][items.enum.discriminant.coercion] of an external enum that contains any non-exhaustive variants. > [!EXAMPLE] > The following enum can be cast because it doesn't contain any non-exhaustive variants: From 004472bfb60198a9ce07e2cefe2db6fddd9d0a38 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:53:31 -0700 Subject: [PATCH 12/17] Split enum-exhaustiveness into a separate rule The existing rule was mixing pattern match restrictions with match arm exhaustiveness. Although very closely related, I think they deserve to be split up. This also adds a little clarity about tuple matching. --- src/attributes/type_system.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 15a56e14f4..12f859142e 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -135,10 +135,9 @@ The implicitly defined same-named constant of a [unit-like struct][struct], or t > ``` r[attributes.type-system.non_exhaustive.match] -There are limitations when matching on non-exhaustive types outside of the defining crate: +When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]) from an external crate, a [StructPattern] must be used which must include a [`..` rest pattern][patterns.rest]. -- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]), a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's [visibility] is reduced to be no greater than `pub(crate)`. -- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not contribute towards the exhaustiveness of the arms. +Because a tuple enum variant's constructor's [visibility] is reduced to be no greater than `pub(crate)`, a [tuple struct pattern][patterns.tuple-struct] cannot be used in a pattern; a [StructPattern] with tuple indexes must be used instead. > [!EXAMPLE] > Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of matching do not compile when outside the defining crate: @@ -149,13 +148,6 @@ There are limitations when matching on non-exhaustive types outside of the defin > // `#[non_exhaustive]`. > use upstream::{Config, Token, Id, Error, Message}; > -> // Cannot match on a non-exhaustive enum without including a wildcard arm. -> match error { -> Error::Message(ref s) => { }, -> Error::Other => { }, -> // would compile with: `_ => {},` -> } -> > // Cannot match on a non-exhaustive struct without a wildcard. > if let Ok(Config { window_width, window_height }) = config { > // would compile with: `..` @@ -174,6 +166,24 @@ There are limitations when matching on non-exhaustive types outside of the defin > // Cannot match on a non-exhaustive tuple or unit enum variant. > Message::Reaction(type) => { }, > Message::Quit => { }, + +r[attributes.type-system.non_exhaustive.enum-exhaustiveness] +When using a [`match` expression][expr.match] on a non-exhaustive [`enum`][enum] from an external crate, matching on a variant does not contribute towards the exhaustiveness of the arms. A [`_` wildcard][patterns.wildcard] arm is needed to make the match exhaustive. + +> [!EXAMPLE] +> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of matching do not compile when outside the defining crate: +> +> +> ```rust, ignore +> // These are types defined in an upstream crate that have been annotated as +> // `#[non_exhaustive]`. +> use upstream::Error; +> +> // Cannot match on a non-exhaustive enum without including a wildcard arm. +> match error { // ERROR: `_` not covered +> Error::Message(ref s) => { }, +> Error::Other => { }, +> // would compile with: `_ => {},` > } > ``` From 1ffeb23419ebc4343166f54b41909c58d76979de Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:54:11 -0700 Subject: [PATCH 13/17] Fix an error in the example with `type` `type` is a keyword and can't be used here. --- src/attributes/type_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 12f859142e..89e513609e 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -164,8 +164,8 @@ Because a tuple enum variant's constructor's [visibility] is reduced to be no gr > // Cannot match on a non-exhaustive struct enum variant without including a wildcard. > Message::Send { from, to, contents } => { }, > // Cannot match on a non-exhaustive tuple or unit enum variant. -> Message::Reaction(type) => { }, > Message::Quit => { }, +> Message::Reaction(x) => { }, r[attributes.type-system.non_exhaustive.enum-exhaustiveness] When using a [`match` expression][expr.match] on a non-exhaustive [`enum`][enum] from an external crate, matching on a variant does not contribute towards the exhaustiveness of the arms. A [`_` wildcard][patterns.wildcard] arm is needed to make the match exhaustive. From f18e390516f23f56adc04557be3076b8f29fa70f Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:55:20 -0700 Subject: [PATCH 14/17] Fix an error in the unit-like pattern match example Single-segment identifiers are bindings and don't work to match against a Unit struct. --- src/attributes/type_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 89e513609e..16260afae5 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -156,7 +156,7 @@ Because a tuple enum variant's constructor's [visibility] is reduced to be no gr > // Cannot match a non-exhaustive unit-like or tuple struct except by using > // braced struct syntax with a wildcard. > // This would compile as `let Token { .. } = token;` -> let Token = token; +> let upstream::Token = token; > // This would compile as `let Id { 0: id_number, .. } = id;` > let Id(id_number) = id; > From 8f51a2a7140e984981ee7382fa3a5b615378cf7a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:56:11 -0700 Subject: [PATCH 15/17] Add a few inline annotations on the examples The intent here is to highlight exactly where the errors are in the example (and which ones aren't errors!). --- src/attributes/type_system.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 16260afae5..8208fadb50 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -106,32 +106,32 @@ The implicitly defined same-named constant of a [unit-like struct][struct], or t > // Cannot construct an instance of `Config`; if new fields were added in > // a new version of `upstream` then this would fail to compile, so it is > // disallowed. -> let config = Config { window_width: 640, window_height: 480 }; +> let config = Config { window_width: 640, window_height: 480 }; // ERROR > > // Cannot construct an instance of `Token`; if new fields were added, then > // it would not be a unit-like struct any more, so the same-named constant > // created by it being a unit-like struct is not public outside the crate; > // this code fails to compile. -> let token = Token; +> let token = Token; // ERROR > > // Cannot construct an instance of `Id`; if new fields were added, then > // its constructor function signature would change, so its constructor > // function is not public outside the crate; this code fails to compile. -> let id = Id(5); +> let id = Id(5); // ERROR > > // Can construct an instance of `Error`; new variants being introduced would > // not result in this failing to compile. -> let error = Error::Message("foo".to_string()); +> let error = Error::Message("foo".to_string()); // Ok > > // Cannot construct an instance of `Message::Send` or `Message::Reaction`; > // if new fields were added in a new version of `upstream` then this would > // fail to compile, so it is disallowed. -> let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), }; -> let message = Message::Reaction(0); +> let message = Message::Send { from: 0, to: 1, contents: "foo".to_string() }; // ERROR +> let message = Message::Reaction(0); // ERROR > > // Cannot construct an instance of `Message::Quit`; if this were converted to > // a tuple enum variant `upstream`, this would fail to compile. -> let message = Message::Quit; +> let message = Message::Quit; // ERROR > ``` r[attributes.type-system.non_exhaustive.match] @@ -149,23 +149,25 @@ Because a tuple enum variant's constructor's [visibility] is reduced to be no gr > use upstream::{Config, Token, Id, Error, Message}; > > // Cannot match on a non-exhaustive struct without a wildcard. -> if let Ok(Config { window_width, window_height }) = config { +> if let Ok(Config { window_width, window_height }) = config { // ERROR: `..` required > // would compile with: `..` > } > > // Cannot match a non-exhaustive unit-like or tuple struct except by using > // braced struct syntax with a wildcard. > // This would compile as `let Token { .. } = token;` -> let upstream::Token = token; +> let upstream::Token = token; // ERROR > // This would compile as `let Id { 0: id_number, .. } = id;` -> let Id(id_number) = id; +> let Id(id_number) = id; // ERROR > > match message { > // Cannot match on a non-exhaustive struct enum variant without including a wildcard. -> Message::Send { from, to, contents } => { }, +> Message::Send { from, to, contents } => { }, // ERROR: `..` required > // Cannot match on a non-exhaustive tuple or unit enum variant. -> Message::Quit => { }, -> Message::Reaction(x) => { }, +> Message::Reaction(x) => { }, // ERROR +> Message::Quit => { }, // ERROR +> } +> ``` r[attributes.type-system.non_exhaustive.enum-exhaustiveness] When using a [`match` expression][expr.match] on a non-exhaustive [`enum`][enum] from an external crate, matching on a variant does not contribute towards the exhaustiveness of the arms. A [`_` wildcard][patterns.wildcard] arm is needed to make the match exhaustive. From 52b46bdbe2983e504a507214c98c1e7f56a6b0ec Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 14:10:21 -0700 Subject: [PATCH 16/17] Linkify attribute --- src/attributes/type_system.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 8208fadb50..5427c8081b 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -7,7 +7,7 @@ r[attributes.type-system.non_exhaustive] ## The `non_exhaustive` attribute r[attributes.type-system.non_exhaustive.intro] -The *`non_exhaustive` attribute* indicates that a type or variant may have more fields or variants added in the future. Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. +The *`non_exhaustive` [attribute][attributes]* indicates that a type or variant may have more fields or variants added in the future. Outside of the defining crate, types annotated with `non_exhaustive` have limitations that preserve backwards compatibility when new fields or variants are added. > [!EXAMPLE] > The following non-exhaustive definitions will be used in the examples that follow. From d08637575a8780bfb3e6dc6a35384def7d0f592f Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 29 Sep 2025 00:45:26 -0700 Subject: [PATCH 17/17] Update non_exhaustive for attribute template changes --- src/attributes/type_system.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 5427c8081b..d2b0d8a57a 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -3,6 +3,7 @@ r[attributes.type-system] The following [attributes] are used for changing how a type can be used. + r[attributes.type-system.non_exhaustive] ## The `non_exhaustive` attribute @@ -39,19 +40,19 @@ The *`non_exhaustive` [attribute][attributes]* indicates that a type or variant > ``` r[attributes.type-system.non_exhaustive.syntax] -The `non_exhaustive` attribute uses the [MetaWord] syntax and thus does not take any inputs. +The `non_exhaustive` attribute uses the [MetaWord] syntax. r[attributes.type-system.non_exhaustive.allowed-positions] The `non_exhaustive` attribute may only be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants. > [!NOTE] -> `rustc` currently warns in some other positions, but this may be rejected in the future. +> `rustc` ignores use in other positions but lints against it. This may become an error in the future. r[attributes.type-system.non_exhaustive.duplicates] -Duplicate instances of the `non_exhaustive` attribute have no effect. +The `non_exhaustive` attribute may be used any number of times on a form. > [!NOTE] -> `rustc` warns on subsequent duplicate `non_exhaustive` attributes. +> `rustc` lints against any use following the first. r[attributes.type-system.non_exhaustive.same-crate] Within the defining crate, `non_exhaustive` has no effect.