Skip to content

Commit c455de9

Browse files
committed
Auto merge of #10877 - obi1kenobi:adding-non-exhaustive, r=weihanglo
Document that adding `#[non_exhaustive]` on existing items is breaking. ### What does this PR try to resolve? Adding `#[non_exhaustive]` to an existing struct, enum, or variant is almost always a breaking change and requires a major version bump for semver purposes. This PR adds a section to the semver reference page that describes this and provides examples showing how `#[non_exhaustive]` can break code. ### Additional information Adding `#[non_exhaustive]` to a unit struct currently has no effect on whether that struct can be constructed in downstream crates. This is inconsistent with the behavior of `#[non_exhaustive]` on unit enum variants, which may not be constructed outside their own crate. This might be due to a similar underlying cause as: rust-lang/rust#78586 The confusing "variant is private" error messages for non-exhaustive unit and tuple variants are a known issue tracked in: rust-lang/rust#82788 Checking for the struct portion of this semver rule is done in: obi1kenobi/cargo-semver-checks#4
2 parents 93f7b78 + f18f7f1 commit c455de9

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

src/doc/src/reference/semver.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ considered incompatible.
9090
* [Major: generalizing a function to use generics with type mismatch](#fn-generalize-mismatch)
9191
* Attributes
9292
* [Major: switching from `no_std` support to requiring `std`](#attr-no-std-to-std)
93+
* [Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields](#attr-adding-non-exhaustive)
9394
* Tooling and environment compatibility
9495
* [Possibly-breaking: changing the minimum version of Rust required](#env-new-rust)
9596
* [Possibly-breaking: changing the platform and environment requirements](#env-change-requirements)
@@ -1115,6 +1116,89 @@ Mitigation strategies:
11151116
optionally enables `std` support, and when the feature is off, the library
11161117
can be used in a `no_std` environment.
11171118

1119+
<a id="attr-adding-non-exhaustive"></a>
1120+
### Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields
1121+
1122+
Making items [`#[non_exhaustive]`][non_exhaustive] changes how they may
1123+
be used outside the crate where they are defined:
1124+
1125+
- Non-exhaustive structs and enum variants cannot be constructed
1126+
using [struct literal] syntax, including [functional update syntax].
1127+
- Pattern matching on non-exhaustive structs requires `..` and
1128+
matching on enums does not count towards exhaustiveness.
1129+
- Casting enum variants to their discriminant with `as` is not allowed.
1130+
1131+
Structs with private fields cannot be constructed using [struct literal] syntax
1132+
regardless of whether [`#[non_exhaustive]`][non_exhaustive] is used.
1133+
Adding [`#[non_exhaustive]`][non_exhaustive] to such a struct is not
1134+
a breaking change.
1135+
1136+
```rust,ignore
1137+
// MAJOR CHANGE
1138+
1139+
///////////////////////////////////////////////////////////
1140+
// Before
1141+
pub struct Foo {
1142+
pub bar: usize,
1143+
}
1144+
1145+
pub enum Bar {
1146+
X,
1147+
Y(usize),
1148+
Z { a: usize },
1149+
}
1150+
1151+
pub enum Quux {
1152+
Var,
1153+
}
1154+
1155+
///////////////////////////////////////////////////////////
1156+
// After
1157+
#[non_exhaustive]
1158+
pub struct Foo {
1159+
pub bar: usize,
1160+
}
1161+
1162+
pub enum Bar {
1163+
#[non_exhaustive]
1164+
X,
1165+
1166+
#[non_exhaustive]
1167+
Y(usize),
1168+
1169+
#[non_exhaustive]
1170+
Z { a: usize },
1171+
}
1172+
1173+
#[non_exhaustive]
1174+
pub enum Quux {
1175+
Var,
1176+
}
1177+
1178+
///////////////////////////////////////////////////////////
1179+
// Example usage that will break.
1180+
use updated_crate::{Bar, Foo, Quux};
1181+
1182+
fn main() {
1183+
let foo = Foo { bar: 0 }; // Error: cannot create non-exhaustive struct using struct expression
1184+
1185+
let bar_x = Bar::X; // Error: unit variant `X` is private
1186+
let bar_y = Bar::Y(0); // Error: tuple variant `Y` is private
1187+
let bar_z = Bar::Z { a: 0 }; // Error: cannot create non-exhaustive variant using struct expression
1188+
1189+
let q = Quux::Var;
1190+
match q {
1191+
Quux::Var => 0,
1192+
// Error: non-exhaustive patterns: `_` not covered
1193+
};
1194+
}
1195+
```
1196+
1197+
Mitigation strategies:
1198+
* Mark structs, enums, and enum variants as
1199+
[`#[non_exhaustive]`][non_exhaustive] when first introducing them,
1200+
rather than adding [`#[non_exhaustive]`][non_exhaustive] later on.
1201+
11181202
## Tooling and environment compatibility
11191203

11201204
<a id="env-new-rust"></a>
@@ -1393,6 +1477,7 @@ document what your commitments are.
13931477
[Default]: ../../std/default/trait.Default.html
13941478
[deprecated]: ../../reference/attributes/diagnostics.html#the-deprecated-attribute
13951479
[disambiguation syntax]: ../../reference/expressions/call-expr.html#disambiguating-function-calls
1480+
[functional update syntax]: ../../reference/expressions/struct-expr.html#functional-update-syntax
13961481
[inherent implementations]: ../../reference/items/implementations.html#inherent-implementations
13971482
[items]: ../../reference/items.html
13981483
[non_exhaustive]: ../../reference/attributes/type_system.html#the-non_exhaustive-attribute

0 commit comments

Comments
 (0)