Skip to content

Commit 585b9bc

Browse files
authored
Merge pull request #1491 from kpreid/neunit
Document how `non_exhaustive` interacts with tuple and unit-like structs.
2 parents a60221a + 076a798 commit 585b9bc

File tree

1 file changed

+52
-15
lines changed

1 file changed

+52
-15
lines changed

src/attributes/type_system.md

+52-15
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ pub struct Config {
2020
pub window_height: u16,
2121
}
2222

23+
#[non_exhaustive]
24+
pub struct Token;
25+
26+
#[non_exhaustive]
27+
pub struct Id(pub u64);
28+
2329
#[non_exhaustive]
2430
pub enum Error {
2531
Message(String),
@@ -34,11 +40,13 @@ pub enum Message {
3440

3541
// Non-exhaustive structs can be constructed as normal within the defining crate.
3642
let config = Config { window_width: 640, window_height: 480 };
43+
let token = Token;
44+
let id = Id(4);
3745

3846
// Non-exhaustive structs can be matched on exhaustively within the defining crate.
39-
if let Config { window_width, window_height } = config {
40-
// ...
41-
}
47+
let Config { window_width, window_height } = config;
48+
let Token = token;
49+
let Id(id_number) = id;
4250

4351
let error = Error::Other;
4452
let message = Message::Reaction(3);
@@ -64,47 +72,68 @@ Non-exhaustive types cannot be constructed outside of the defining crate:
6472

6573
- Non-exhaustive variants ([`struct`][struct] or [`enum` variant][enum]) cannot be constructed
6674
with a [_StructExpression_] \(including with [functional update syntax]).
75+
- The implicitly defined same-named constant of a [unit-like struct][struct],
76+
or the same-named constructor function of a [tuple struct][struct],
77+
has a [visibility] no greater than `pub(crate)`.
78+
That is, if the struct’s visibility is `pub`, then the constant or constructor’s visibility
79+
is `pub(crate)`, and otherwise the visibility of the two items is the same
80+
(as is the case without `#[non_exhaustive]`).
6781
- [`enum`][enum] instances can be constructed.
6882

83+
The following examples of construction do not compile when outside the defining crate:
84+
6985
<!-- ignore: requires external crates -->
7086
```rust,ignore
71-
// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been
72-
// annotated as `#[non_exhaustive]`.
73-
use upstream::{Config, Error, Message};
87+
// These are types defined in an upstream crate that have been annotated as
88+
// `#[non_exhaustive]`.
89+
use upstream::{Config, Token, Id, Error, Message};
7490
75-
// Cannot construct an instance of `Config`, if new fields were added in
91+
// Cannot construct an instance of `Config`; if new fields were added in
7692
// a new version of `upstream` then this would fail to compile, so it is
7793
// disallowed.
7894
let config = Config { window_width: 640, window_height: 480 };
7995
80-
// Can construct an instance of `Error`, new variants being introduced would
96+
// Cannot construct an instance of `Token`; if new fields were added, then
97+
// it would not be a unit-like struct any more, so the same-named constant
98+
// created by it being a unit-like struct is not public outside the crate;
99+
// this code fails to compile.
100+
let token = Token;
101+
102+
// Cannot construct an instance of `Id`; if new fields were added, then
103+
// its constructor function signature would change, so its constructor
104+
// function is not public outside the crate; this code fails to compile.
105+
let id = Id(5);
106+
107+
// Can construct an instance of `Error`; new variants being introduced would
81108
// not result in this failing to compile.
82109
let error = Error::Message("foo".to_string());
83110
84-
// Cannot construct an instance of `Message::Send` or `Message::Reaction`,
111+
// Cannot construct an instance of `Message::Send` or `Message::Reaction`;
85112
// if new fields were added in a new version of `upstream` then this would
86113
// fail to compile, so it is disallowed.
87114
let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
88115
let message = Message::Reaction(0);
89116
90-
// Cannot construct an instance of `Message::Quit`, if this were converted to
117+
// Cannot construct an instance of `Message::Quit`; if this were converted to
91118
// a tuple-variant `upstream` then this would fail to compile.
92119
let message = Message::Quit;
93120
```
94121

95122
There are limitations when matching on non-exhaustive types outside of the defining crate:
96123

97124
- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]),
98-
a [_StructPattern_] must be used which must include a `..`. Tuple variant constructor visibility
99-
is lowered to `min($vis, pub(crate))`.
125+
a [_StructPattern_] must be used which must include a `..`. A tuple variant's constructor's
126+
[visibility] is reduced to be no greater than `pub(crate)`.
100127
- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not
101128
contribute towards the exhaustiveness of the arms.
102129

130+
The following examples of matching do not compile when outside the defining crate:
131+
103132
<!-- ignore: requires external crates -->
104133
```rust, ignore
105-
// `Config`, `Error`, and `Message` are types defined in an upstream crate that have been
106-
// annotated as `#[non_exhaustive]`.
107-
use upstream::{Config, Error, Message};
134+
// These are types defined in an upstream crate that have been annotated as
135+
// `#[non_exhaustive]`.
136+
use upstream::{Config, Token, Id, Error, Message};
108137
109138
// Cannot match on a non-exhaustive enum without including a wildcard arm.
110139
match error {
@@ -118,6 +147,13 @@ if let Ok(Config { window_width, window_height }) = config {
118147
// would compile with: `..`
119148
}
120149
150+
// Cannot match a non-exhaustive unit-like or tuple struct except by using
151+
// braced struct syntax with a wildcard.
152+
// This would compile as `let Token { .. } = token;`
153+
let Token = token;
154+
// This would compile as `let Id { 0: id_number, .. } = id;`
155+
let Id(id_number) = id;
156+
121157
match message {
122158
// Cannot match on a non-exhaustive struct enum variant without including a wildcard.
123159
Message::Send { from, to, contents } => { },
@@ -147,3 +183,4 @@ Non-exhaustive types are always considered inhabited in downstream crates.
147183
[enum]: ../items/enumerations.md
148184
[functional update syntax]: ../expressions/struct-expr.md#functional-update-syntax
149185
[struct]: ../items/structs.md
186+
[visibility]: ../visibility-and-privacy.md

0 commit comments

Comments
 (0)