From 61cd0a84d775833c3a584f391781439c84a87142 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 10:33:55 -0400 Subject: [PATCH 1/7] Document that adding #[non_exhaustive] is a major breaking change. --- src/doc/src/reference/semver.md | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 22c42610f17..98b888e5749 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -90,6 +90,7 @@ considered incompatible. * [Major: generalizing a function to use generics with type mismatch](#fn-generalize-mismatch) * Attributes * [Major: switching from `no_std` support to requiring `std`](#attr-no-std-to-std) + * [Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields](#attr-adding-non-exhaustive) * Tooling and environment compatibility * [Possibly-breaking: changing the minimum version of Rust required](#env-new-rust) * [Possibly-breaking: changing the platform and environment requirements](#env-change-requirements) @@ -1115,6 +1116,81 @@ Mitigation strategies: optionally enables `std` support, and when the feature is off, the library can be used in a `no_std` environment. + +### Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields + +Making items [`#[non_exhaustive]`][non_exhaustive] changes how they may +be used outside the crate where they are defined: +- Non-exhaustive structs and enum variants cannot be constructed + using [struct literal] syntax, including [functional update syntax]. +- Pattern matching on non-exhaustive enums always requires + a wildcard (`_`) arm. + +Structs with private fields cannot be constructed using [struct literal] syntax +regardless of whether [`#[non_exhaustive]`][non_exhaustive] is used. +Adding [`#[non_exhaustive]`][non_exhaustive] to such a struct is not +a breaking change. + +```rust,ignore +// MAJOR CHANGE + +/////////////////////////////////////////////////////////// +// Before +pub struct Foo { + pub bar: usize, +} + +pub enum Bar { + Baz, +} + +pub enum Quux { + X, + Y(a: usize), + Z { b: usize }, +} + +/////////////////////////////////////////////////////////// +// After +#[non_exhaustive] +pub struct Foo { + pub bar: usize, +} + +pub enum Bar { + #[non_exhaustive] X, + #[non_exhaustive] Y(usize), + #[non_exhaustive] Z { a: usize }, +} + +#[non_exhaustive] +pub enum Quux { + Var, +} + +/////////////////////////////////////////////////////////// +// Example usage that will break. +use updated_crate::{Foo, Bar, Quux}; + +fn main() { + let foo = Foo { bar: 0 }; // Error: cannot create non-exhaustive struct using struct expression + + let bar_x = Bar::X; // Error: unit variant `X` is private + let bar_y = Bar::Y(0); // Error: tuple variant `Y` is private + let bar_z = Bar::Z { a: 0 }; // Error: cannot create non-exhaustive variant using struct expression + + let q = Quux::Var; + match q { // Error: non-exhaustive patterns: `_` not covered + Quux::Var => 0, + } +} +``` + +Mitigation strategies: +* Mark structs, enums, and enum variants as + [`#[non_exhaustive]`][non_exhaustive] when first introducing them, + rather than adding [`#[non_exhaustive]`][non_exhaustive] later on. + ## Tooling and environment compatibility @@ -1393,6 +1469,7 @@ document what your commitments are. [Default]: ../../std/default/trait.Default.html [deprecated]: ../../reference/attributes/diagnostics.html#the-deprecated-attribute [disambiguation syntax]: ../../reference/expressions/call-expr.html#disambiguating-function-calls +[functional update syntax]: ../../reference/expressions/struct-expr.html#functional-update-syntax [inherent implementations]: ../../reference/items/implementations.html#inherent-implementations [items]: ../../reference/items.html [non_exhaustive]: ../../reference/attributes/type_system.html#the-non_exhaustive-attribute From fc77710e07fba4fbe84d45cad3d8d94a1bb60015 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 11:06:50 -0400 Subject: [PATCH 2/7] Fix example code. --- src/doc/src/reference/semver.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 98b888e5749..4809935277e 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1146,8 +1146,8 @@ pub enum Bar { pub enum Quux { X, - Y(a: usize), - Z { b: usize }, + Y(usize), + Z { a: usize }, } /////////////////////////////////////////////////////////// From 08c4f92273cef1cdcf6968256546bd3150d93e1d Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 11:40:27 -0400 Subject: [PATCH 3/7] Tweak example formatting. --- src/doc/src/reference/semver.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 4809935277e..6bc244fbc07 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1180,8 +1180,9 @@ fn main() { let bar_z = Bar::Z { a: 0 }; // Error: cannot create non-exhaustive variant using struct expression let q = Quux::Var; - match q { // Error: non-exhaustive patterns: `_` not covered + match q { Quux::Var => 0, + // Error: non-exhaustive patterns: `_` not covered } } ``` From 512f12246e20fcd30be6287bcdb5021d39558c8c Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 11:43:20 -0400 Subject: [PATCH 4/7] More formatting tweaks. --- src/doc/src/reference/semver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 6bc244fbc07..92752486b26 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1170,7 +1170,7 @@ pub enum Quux { /////////////////////////////////////////////////////////// // Example usage that will break. -use updated_crate::{Foo, Bar, Quux}; +use updated_crate::{Bar, Foo, Quux}; fn main() { let foo = Foo { bar: 0 }; // Error: cannot create non-exhaustive struct using struct expression From 2451c2d3ba51c673570f07be401071b7f9f2ef59 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 11:54:35 -0400 Subject: [PATCH 5/7] Match up enums in the example. --- src/doc/src/reference/semver.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 92752486b26..6c8308403d9 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1141,15 +1141,15 @@ pub struct Foo { } pub enum Bar { - Baz, -} - -pub enum Quux { X, Y(usize), Z { a: usize }, } +pub enum Quux { + Var, +} + /////////////////////////////////////////////////////////// // After #[non_exhaustive] @@ -1183,7 +1183,7 @@ fn main() { match q { Quux::Var => 0, // Error: non-exhaustive patterns: `_` not covered - } + }; } ``` From c4ce40c37f7a74615eb743675c8042af96f13b16 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 Jul 2022 11:58:21 -0400 Subject: [PATCH 6/7] Fix non_exhaustive formatting to rustfmt's liking. --- src/doc/src/reference/semver.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 6c8308403d9..5336806502f 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1158,9 +1158,14 @@ pub struct Foo { } pub enum Bar { - #[non_exhaustive] X, - #[non_exhaustive] Y(usize), - #[non_exhaustive] Z { a: usize }, + #[non_exhaustive] + X, + + #[non_exhaustive] + Y(usize), + + #[non_exhaustive] + Z { a: usize }, } #[non_exhaustive] From f18f7f12409814fc3debf9423f84addc39aca772 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 1 May 2023 11:15:37 -0700 Subject: [PATCH 7/7] Add some clarifications on what happens when `non_exhaustive` is added. --- src/doc/src/reference/semver.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index 5336806502f..799438967a1 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1121,10 +1121,12 @@ Mitigation strategies: Making items [`#[non_exhaustive]`][non_exhaustive] changes how they may be used outside the crate where they are defined: + - Non-exhaustive structs and enum variants cannot be constructed using [struct literal] syntax, including [functional update syntax]. -- Pattern matching on non-exhaustive enums always requires - a wildcard (`_`) arm. +- Pattern matching on non-exhaustive structs requires `..` and + matching on enums does not count towards exhaustiveness. +- Casting enum variants to their discriminant with `as` is not allowed. Structs with private fields cannot be constructed using [struct literal] syntax regardless of whether [`#[non_exhaustive]`][non_exhaustive] is used.