Skip to content

Commit

Permalink
Auto merge of #10877 - obi1kenobi:adding-non-exhaustive, r=weihanglo
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed May 1, 2023
2 parents 93f7b78 + f18f7f1 commit c455de9
Showing 1 changed file with 85 additions and 0 deletions.
85 changes: 85 additions & 0 deletions src/doc/src/reference/semver.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -1115,6 +1116,89 @@ Mitigation strategies:
optionally enables `std` support, and when the feature is off, the library
can be used in a `no_std` environment.

<a id="attr-adding-non-exhaustive"></a>
### 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 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.
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 {
X,
Y(usize),
Z { a: usize },
}
pub enum Quux {
Var,
}
///////////////////////////////////////////////////////////
// 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::{Bar, Foo, 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 {
Quux::Var => 0,
// Error: non-exhaustive patterns: `_` not covered
};
}
```

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

<a id="env-new-rust"></a>
Expand Down Expand Up @@ -1393,6 +1477,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
Expand Down

0 comments on commit c455de9

Please sign in to comment.