Skip to content

Commit c358154

Browse files
author
Clar Charr
committed
Replace Generics example with a config, add note on struct patterns.
1 parent c9c82cf commit c358154

File tree

1 file changed

+66
-44
lines changed

1 file changed

+66
-44
lines changed

text/0000-non-exhaustive.md

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -208,56 +208,55 @@ other use cases.
208208
## A case for structs
209209

210210
In addition to enums, it makes sense to extend this feature to structs as well.
211-
For example, consider [`syn::Generics`]:
211+
The most common use case for this would be config structs, like the one below:
212212

213213
```rust
214-
pub struct Generics {
215-
pub lifetimes: Vec<LifetimeDef>,
216-
pub ty_params: Vec<TyParam>,
217-
pub where_clause: WhereClause,
214+
pub struct Config {
215+
pub window_width: u16,
216+
pub window_height: u16,
218217
}
219218
```
220219

221-
Let's say that the language introduces `use` clauses, as described by
222-
@petrochenkov [here][use clauses]. Now, it seems natural to add a new field to
223-
this struct:
220+
As this configuration struct gets larger, it makes sense that more fields will
221+
be added. In the future, the crate may decide to add more public fields, or some
222+
private fields. For example, let's assume we make the following addition:
224223

225224
```rust
226-
pub struct Generics {
227-
pub lifetimes: Vec<LifetimeDef>,
228-
pub ty_params: Vec<TyParam>,
229-
pub where_clause: WhereClause,
230-
pub use_clause: UseClause,
225+
pub struct Config {
226+
pub window_width: u16,
227+
pub window_height: u16,
228+
pub is_fullscreen: bool,
231229
}
232230
```
233231

234-
Unfortunately, this is a breaking change. Because we have full knowledge of the
235-
fields in the struct, we can construct `Generics` without a special constructor:
232+
Now, code that constructs the struct, like below, will fail to compile:
236233

237-
```rust
238-
let gens = Generics {
239-
lifetimes: Vec::new(),
240-
ty_params: Vec::new(),
241-
where_clause: WhereClause::none(),
242-
};
234+
```
235+
let config = Config { window_width: 640, window_height: 480 };
243236
```
244237

245-
If we add this field, this will turn into a compiler error; we didn't add a
246-
value for the use clause!
238+
And code that matches the struct, like below, will also fail to compile:
239+
240+
```rust
241+
if let Ok(Config { window_width, window_height }) = load_config() {
242+
// ...
243+
}
244+
```
247245

248-
To rectify this, we can add a private field to the struct:
246+
Adding this new setting is now a breaking change! To rectify this, we could
247+
always add a private field:
249248

250249
```rust
251-
pub struct Generics {
252-
pub lifetimes: Vec<LifetimeDef>,
253-
pub ty_params: Vec<TyParam>,
254-
pub where_clause: WhereClause,
250+
pub struct Config {
251+
pub window_width: u16,
252+
pub window_height: u16,
253+
pub is_fullscreen: bool,
255254
non_exhaustive: (),
256255
}
257256
```
258257

259-
But this makes it more difficult for the crate itself to construct `Generics`,
260-
because they have to add a `non_exhaustive: ()` field every time they make a new
258+
But this makes it more difficult for the crate itself to construct `Config`,
259+
because you have to add a `non_exhaustive: ()` field every time you make a new
261260
value.
262261

263262
# Detailed design
@@ -316,32 +315,56 @@ dead and remove it.
316315

317316
Like with enums, the attribute is essentially ignored in the crate that defines
318317
the struct, so that users can continue to construct values for the struct.
319-
However, this will prevent downstream users from constructing values, because
320-
fields may be added to the struct in the future.
318+
However, this will prevent downstream users from constructing values or
319+
exhaustively matching values, because fields may be added to the struct in the
320+
future.
321321

322-
For example, using [`syn::Generics`] again:
322+
For example, using our `Config` again:
323323

324324
```rust
325325
#[non_exhaustive]
326-
pub struct Generics {
327-
pub lifetimes: Vec<LifetimeDef>,
328-
pub ty_params: Vec<TyParam>,
329-
pub where_clause: WhereClause,
326+
pub struct Config {
327+
pub window_width: u16,
328+
pub window_height: u16,
330329
}
331330
```
332331

333-
This will still allow the crate to create values of `Generics`, but it will
334-
prevent the user from doing so, because more fields might be added in the
335-
future. Similarly, this will prevent functional record updates like the below:
332+
We can still construct our config within the defining crate like so:
333+
334+
```rust
335+
let config = Config { window_width: 640, window_height: 480 };
336+
```
337+
338+
And we can even exhaustively match on it, like so:
339+
340+
```rust
341+
if let Ok(Config { window_width, window_height }) = load_config() {
342+
// ...
343+
}
344+
```
345+
346+
But users outside the crate won't be able to construct their own values, because
347+
otherwise, adding extra fields would be a breaking change.
348+
349+
Users can still match on `Config`s non-exhaustively, as usual:
350+
351+
```rust
352+
let &Config { window_width, window_height, .. } = config;
353+
```
354+
355+
But without the `..`, this code will fail to compile. Additionally, the user
356+
won't be able to perform any functional-record-updates like the below:
336357

337358
```rust
338-
let val = Generics {
339-
lifetimes: Vec::new(),
359+
let val = Config {
360+
window_width: 640,
361+
window_height: 480,
340362
..Default::default()
341363
};
342364
```
343365

344-
Because this would require full knowledge of all of the fields of the struct.
366+
Because there's no guarantee that the remaining fields will satisfy the
367+
requirements (in this case, `Default`).
345368

346369
Although it should not be explicitly forbidden by the language to mark a struct
347370
with some private fields as non-exhaustive, it should emit a warning to tell the
@@ -455,6 +478,5 @@ implemented downstream.
455478
[RFC 757]: https://github.com/rust-lang/rfcs/pull/757
456479
[`std::io::ErrorKind`]: https://doc.rust-lang.org/1.17.0/std/io/enum.ErrorKind.html
457480
[`diesel::result::Error`]: https://docs.rs/diesel/0.13.0/diesel/result/enum.Error.html
458-
[`syn::Generics`]: https://dtolnay.github.io/syn/syn/struct.Generics.html
459481
[use clauses]: https://github.com/rust-lang/rfcs/pull/1976#issuecomment-301903528
460482
[`byteorder`]: https://github.com/BurntSushi/byteorder/tree/f8e7685b3a81c52f5448fd77fb4e0535bc92f880

0 commit comments

Comments
 (0)