From dbe211f72d346768ef18155c876b01e914448b81 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:27:26 -0700 Subject: [PATCH 01/16] Move non_exhaustive examples into example blocks --- src/attributes/type_system.md | 312 +++++++++++++++++----------------- 1 file changed, 159 insertions(+), 153 deletions(-) diff --git a/src/attributes/type_system.md b/src/attributes/type_system.md index 0a3d4f15f..026fbaa3f 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,44 +92,45 @@ 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-variant `upstream` then 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-variant `upstream` then 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: @@ -137,72 +141,74 @@ There are limitations when matching on non-exhaustive types outside of the defin - 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 => { }, -} -``` +> [!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 66ad741dd0a6d1f0cc13e180dfc0362e691f9782 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:30:38 -0700 Subject: [PATCH 02/16] 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 026fbaa3f..46b01db54 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 0da3809a249601974d22df96560dcd47f71ddb19 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:32:15 -0700 Subject: [PATCH 03/16] 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 46b01db54..40de52073 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 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 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 561b743dcf102e6811a2982a97b1389a887a9569 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:33:41 -0700 Subject: [PATCH 04/16] 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 40de52073..bc47ec33d 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 6fd4e1384784709a147bf3224b00c1ed0576a4eb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:36:29 -0700 Subject: [PATCH 05/16] 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 bc47ec33d..3e4f78fc7 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 e7afb96280c5175c230b8758c6003615456f96d1 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:37:31 -0700 Subject: [PATCH 06/16] 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 3e4f78fc7..d3f3dbd43 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 c4a2557318b2756919c2f2fad005ccf83be81bd6 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:38:28 -0700 Subject: [PATCH 07/16] 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 d3f3dbd43..25902a4c3 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 671f4db5cb6fdc0b27f164656533f421412b7a29 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:38:53 -0700 Subject: [PATCH 08/16] 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 25902a4c3..dde37c7ae 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 2742015224de71f3010fd67e68bb11d2c5c1153c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:46:53 -0700 Subject: [PATCH 09/16] 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 dde37c7ae..9df7d33c6 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 069b3455fc322ee759e215f1fc1af96b2160c1dc Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:48:47 -0700 Subject: [PATCH 10/16] 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 9df7d33c6..87e1f0020 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 1ce64af651927d7717017f2d5280c06026046fa7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:49:47 -0700 Subject: [PATCH 11/16] 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 87e1f0020..d59364cb5 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 0b5c99f3f259da00ff9ec292cfdef438e1b5d67a Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:53:31 -0700 Subject: [PATCH 12/16] 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 d59364cb5..0f973ed86 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 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 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 4cea50cee118aca0eda52d15b393d760f800dc13 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:54:11 -0700 Subject: [PATCH 13/16] 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 0f973ed86..8f5b1e442 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -164,8 +164,8 @@ Because a tuple variant's constructor's [visibility] is reduced to be no greater > // 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 cdfae73a65fdb40fc87a2e682b85a4d425e77aeb Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:55:20 -0700 Subject: [PATCH 14/16] 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 8f5b1e442..2d004c68c 100644 --- a/src/attributes/type_system.md +++ b/src/attributes/type_system.md @@ -156,7 +156,7 @@ Because a tuple variant's constructor's [visibility] is reduced to be no greater > // 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 86210e58d6883f14c09cba896acb16c86e65b813 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 13:56:11 -0700 Subject: [PATCH 15/16] 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 2d004c68c..bc6688502 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-variant `upstream` then 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 variant's constructor's [visibility] is reduced to be no greater > 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 f468b0c22e9bb69bab111fc2898642f44f0af076 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 31 May 2025 14:10:21 -0700 Subject: [PATCH 16/16] 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 bc6688502..f25a2c62b 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.