Skip to content

Commit fc0f4e5

Browse files
committed
Move non_exhaustive examples into example blocks
1 parent e11adf6 commit fc0f4e5

File tree

1 file changed

+165
-154
lines changed

1 file changed

+165
-154
lines changed

src/attributes/type_system.md

Lines changed: 165 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,61 @@ r[attributes.type-system.non_exhaustive.intro]
1010
The *`non_exhaustive` attribute* indicates that a type or variant may have
1111
more fields or variants added in the future.
1212

13+
> [!EXAMPLE]
14+
> The following non-exhaustive definitions will be used in the examples that follow.
15+
>
16+
> ```rust
17+
> #[non_exhaustive]
18+
> pub struct Config {
19+
> pub window_width: u16,
20+
> pub window_height: u16,
21+
> }
22+
>
23+
> #[non_exhaustive]
24+
> pub struct Token;
25+
>
26+
> #[non_exhaustive]
27+
> pub struct Id(pub u64);
28+
>
29+
> #[non_exhaustive]
30+
> pub enum Error {
31+
> Message(String),
32+
> Other,
33+
> }
34+
>
35+
> pub enum Message {
36+
> #[non_exhaustive] Send { from: u32, to: u32, contents: String },
37+
> #[non_exhaustive] Reaction(u32),
38+
> #[non_exhaustive] Quit,
39+
> }
40+
>
41+
> // Non-exhaustive structs can be constructed as normal within the defining crate.
42+
> let config = Config { window_width: 640, window_height: 480 };
43+
> let token = Token;
44+
> let id = Id(4);
45+
>
46+
> // Non-exhaustive structs can be matched on exhaustively within the defining crate.
47+
> let Config { window_width, window_height } = config;
48+
> let Token = token;
49+
> let Id(id_number) = id;
50+
>
51+
> let error = Error::Other;
52+
> let message = Message::Reaction(3);
53+
>
54+
> // Non-exhaustive enums can be matched on exhaustively within the defining crate.
55+
> match error {
56+
> Error::Message(ref s) => { },
57+
> Error::Other => { },
58+
> }
59+
>
60+
> match message {
61+
> // Non-exhaustive variants can be matched on exhaustively within the defining crate.
62+
> Message::Send { from, to, contents } => { },
63+
> Message::Reaction(id) => { },
64+
> Message::Quit => { },
65+
> }
66+
> ```
67+
1368
r[attributes.type-system.non_exhaustive.allowed-positions]
1469
It can be applied to [`struct`s][struct], [`enum`s][enum], and `enum` variants.
1570
@@ -20,58 +75,6 @@ take any inputs.
2075
r[attributes.type-system.non_exhaustive.same-crate]
2176
Within the defining crate, `non_exhaustive` has no effect.
2277
23-
```rust
24-
#[non_exhaustive]
25-
pub struct Config {
26-
pub window_width: u16,
27-
pub window_height: u16,
28-
}
29-
30-
#[non_exhaustive]
31-
pub struct Token;
32-
33-
#[non_exhaustive]
34-
pub struct Id(pub u64);
35-
36-
#[non_exhaustive]
37-
pub enum Error {
38-
Message(String),
39-
Other,
40-
}
41-
42-
pub enum Message {
43-
#[non_exhaustive] Send { from: u32, to: u32, contents: String },
44-
#[non_exhaustive] Reaction(u32),
45-
#[non_exhaustive] Quit,
46-
}
47-
48-
// Non-exhaustive structs can be constructed as normal within the defining crate.
49-
let config = Config { window_width: 640, window_height: 480 };
50-
let token = Token;
51-
let id = Id(4);
52-
53-
// Non-exhaustive structs can be matched on exhaustively within the defining crate.
54-
let Config { window_width, window_height } = config;
55-
let Token = token;
56-
let Id(id_number) = id;
57-
58-
let error = Error::Other;
59-
let message = Message::Reaction(3);
60-
61-
// Non-exhaustive enums can be matched on exhaustively within the defining crate.
62-
match error {
63-
Error::Message(ref s) => { },
64-
Error::Other => { },
65-
}
66-
67-
match message {
68-
// Non-exhaustive variants can be matched on exhaustively within the defining crate.
69-
Message::Send { from, to, contents } => { },
70-
Message::Reaction(id) => { },
71-
Message::Quit => { },
72-
}
73-
```
74-
7578
r[attributes.type-system.non_exhaustive.external-crate]
7679
Outside of the defining crate, types annotated with `non_exhaustive` have limitations that
7780
preserve backwards compatibility when new fields or variants are added.
@@ -89,115 +92,123 @@ Non-exhaustive types cannot be constructed outside of the defining crate:
8992
(as is the case without `#[non_exhaustive]`).
9093
- [`enum`][enum] instances can be constructed.
9194
92-
The following examples of construction do not compile when outside the defining crate:
93-
94-
<!-- ignore: requires external crates -->
95-
```rust,ignore
96-
// These are types defined in an upstream crate that have been annotated as
97-
// `#[non_exhaustive]`.
98-
use upstream::{Config, Token, Id, Error, Message};
99-
100-
// Cannot construct an instance of `Config`; if new fields were added in
101-
// a new version of `upstream` then this would fail to compile, so it is
102-
// disallowed.
103-
let config = Config { window_width: 640, window_height: 480 };
104-
105-
// Cannot construct an instance of `Token`; if new fields were added, then
106-
// it would not be a unit-like struct any more, so the same-named constant
107-
// created by it being a unit-like struct is not public outside the crate;
108-
// this code fails to compile.
109-
let token = Token;
110-
111-
// Cannot construct an instance of `Id`; if new fields were added, then
112-
// its constructor function signature would change, so its constructor
113-
// function is not public outside the crate; this code fails to compile.
114-
let id = Id(5);
115-
116-
// Can construct an instance of `Error`; new variants being introduced would
117-
// not result in this failing to compile.
118-
let error = Error::Message("foo".to_string());
119-
120-
// Cannot construct an instance of `Message::Send` or `Message::Reaction`;
121-
// if new fields were added in a new version of `upstream` then this would
122-
// fail to compile, so it is disallowed.
123-
let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
124-
let message = Message::Reaction(0);
125-
126-
// Cannot construct an instance of `Message::Quit`; if this were converted to
127-
// a tuple enum variant `upstream`, this would fail to compile.
128-
let message = Message::Quit;
129-
```
95+
> [!EXAMPLE]
96+
> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of construction do not compile when outside the defining crate:
97+
>
98+
> <!-- ignore: requires external crates -->
99+
> ```rust,ignore
100+
> // These are types defined in an upstream crate that have been annotated as
101+
> // `#[non_exhaustive]`.
102+
> use upstream::{Config, Token, Id, Error, Message};
103+
>
104+
> // Cannot construct an instance of `Config`; if new fields were added in
105+
> // a new version of `upstream` then this would fail to compile, so it is
106+
> // disallowed.
107+
> let config = Config { window_width: 640, window_height: 480 };
108+
>
109+
> // Cannot construct an instance of `Token`; if new fields were added, then
110+
> // it would not be a unit-like struct any more, so the same-named constant
111+
> // created by it being a unit-like struct is not public outside the crate;
112+
> // this code fails to compile.
113+
> let token = Token;
114+
>
115+
> // Cannot construct an instance of `Id`; if new fields were added, then
116+
> // its constructor function signature would change, so its constructor
117+
> // function is not public outside the crate; this code fails to compile.
118+
> let id = Id(5);
119+
>
120+
> // Can construct an instance of `Error`; new variants being introduced would
121+
> // not result in this failing to compile.
122+
> let error = Error::Message("foo".to_string());
123+
>
124+
> // Cannot construct an instance of `Message::Send` or `Message::Reaction`;
125+
> // if new fields were added in a new version of `upstream` then this would
126+
> // fail to compile, so it is disallowed.
127+
> let message = Message::Send { from: 0, to: 1, contents: "foo".to_string(), };
128+
> let message = Message::Reaction(0);
129+
>
130+
> // Cannot construct an instance of `Message::Quit`; if this were converted to
131+
> // a tuple enum variant `upstream`, this would fail to compile.
132+
> let message = Message::Quit;
133+
> ```
130134
131135
r[attributes.type-system.non_exhaustive.match]
132136
There are limitations when matching on non-exhaustive types outside of the defining crate:
133137
134-
- 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 enum variant's constructor's [visibility] is reduced to be no greater than `pub(crate)`.
135-
- 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:
136-
137-
<!-- ignore: requires external crates -->
138-
```rust, ignore
139-
// These are types defined in an upstream crate that have been annotated as
140-
// `#[non_exhaustive]`.
141-
use upstream::{Config, Token, Id, Error, Message};
142-
143-
// Cannot match on a non-exhaustive enum without including a wildcard arm.
144-
match error {
145-
Error::Message(ref s) => { },
146-
Error::Other => { },
147-
// would compile with: `_ => {},`
148-
}
149-
150-
// Cannot match on a non-exhaustive struct without a wildcard.
151-
if let Ok(Config { window_width, window_height }) = config {
152-
// would compile with: `..`
153-
}
154-
155-
// Cannot match a non-exhaustive unit-like or tuple struct except by using
156-
// braced struct syntax with a wildcard.
157-
// This would compile as `let Token { .. } = token;`
158-
let Token = token;
159-
// This would compile as `let Id { 0: id_number, .. } = id;`
160-
let Id(id_number) = id;
161-
162-
match message {
163-
// Cannot match on a non-exhaustive struct enum variant without including a wildcard.
164-
Message::Send { from, to, contents } => { },
165-
// Cannot match on a non-exhaustive tuple or unit enum variant.
166-
Message::Reaction(type) => { },
167-
Message::Quit => { },
168-
}
169-
```
138+
- When pattern matching on a non-exhaustive variant ([`struct`][struct] or [`enum` variant][enum]),
139+
a [StructPattern] must be used which must include a `..`. A tuple enum variant's constructor's
140+
[visibility] is reduced to be no greater than `pub(crate)`.
141+
- When pattern matching on a non-exhaustive [`enum`][enum], matching on a variant does not
142+
contribute towards the exhaustiveness of the arms.
143+
144+
> [!EXAMPLE]
145+
> Using the definitions from [above][attributes.type-system.non_exhaustive.intro], the following examples of matching do not compile when outside the defining crate:
146+
>
147+
> <!-- ignore: requires external crates -->
148+
> ```rust, ignore
149+
> // These are types defined in an upstream crate that have been annotated as
150+
> // `#[non_exhaustive]`.
151+
> use upstream::{Config, Token, Id, Error, Message};
152+
>
153+
> // Cannot match on a non-exhaustive enum without including a wildcard arm.
154+
> match error {
155+
> Error::Message(ref s) => { },
156+
> Error::Other => { },
157+
> // would compile with: `_ => {},`
158+
> }
159+
>
160+
> // Cannot match on a non-exhaustive struct without a wildcard.
161+
> if let Ok(Config { window_width, window_height }) = config {
162+
> // would compile with: `..`
163+
> }
164+
>
165+
> // Cannot match a non-exhaustive unit-like or tuple struct except by using
166+
> // braced struct syntax with a wildcard.
167+
> // This would compile as `let Token { .. } = token;`
168+
> let Token = token;
169+
> // This would compile as `let Id { 0: id_number, .. } = id;`
170+
> let Id(id_number) = id;
171+
>
172+
> match message {
173+
> // Cannot match on a non-exhaustive struct enum variant without including a wildcard.
174+
> Message::Send { from, to, contents } => { },
175+
> // Cannot match on a non-exhaustive tuple or unit enum variant.
176+
> Message::Reaction(type) => { },
177+
> Message::Quit => { },
178+
> }
179+
> ```
170180
171181
It's also not allowed to use numeric casts (`as`) on enums that contain any non-exhaustive variants.
172182
173-
For example, the following enum can be cast because it doesn't contain any non-exhaustive variants:
174-
175-
```rust
176-
#[non_exhaustive]
177-
pub enum Example {
178-
First,
179-
Second
180-
}
181-
```
182-
183-
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:
184-
185-
```rust
186-
#[non_exhaustive]
187-
pub enum EnumWithNonExhaustiveVariants {
188-
First,
189-
#[non_exhaustive]
190-
Second
191-
}
192-
```
193-
194-
<!-- ignore: needs multiple crates -->
195-
```rust,ignore
196-
use othercrate::EnumWithNonExhaustiveVariants;
197-
198-
// Error: cannot cast an enum with a non-exhaustive variant when it's defined in another crate
199-
let _ = EnumWithNonExhaustiveVariants::First as u8;
200-
```
183+
> [!EXAMPLE]
184+
> The following enum can be cast because it doesn't contain any non-exhaustive variants:
185+
>
186+
> ```rust
187+
> #[non_exhaustive]
188+
> pub enum Example {
189+
> First,
190+
> Second
191+
> }
192+
> ```
193+
>
194+
> 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:
195+
>
196+
> ```rust
197+
> #[non_exhaustive]
198+
> pub enum EnumWithNonExhaustiveVariants {
199+
> First,
200+
> #[non_exhaustive]
201+
> Second
202+
> }
203+
> ```
204+
>
205+
> <!-- ignore: needs multiple crates -->
206+
> ```rust,ignore
207+
> use othercrate::EnumWithNonExhaustiveVariants;
208+
>
209+
> // Error: cannot cast an enum with a non-exhaustive variant when it's defined in another crate
210+
> let _ = EnumWithNonExhaustiveVariants::First as u8;
211+
> ```
201212
202213
Non-exhaustive types are always considered inhabited in downstream crates.
203214

0 commit comments

Comments
 (0)