@@ -208,56 +208,55 @@ other use cases.
208
208
## A case for structs
209
209
210
210
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 :
212
212
213
213
``` 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 ,
218
217
}
219
218
```
220
219
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 :
224
223
225
224
``` 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 ,
231
229
}
232
230
```
233
231
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:
236
233
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 };
243
236
```
244
237
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
+ ```
247
245
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:
249
248
250
249
``` 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 ,
255
254
non_exhaustive : (),
256
255
}
257
256
```
258
257
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
261
260
value.
262
261
263
262
# Detailed design
@@ -316,32 +315,56 @@ dead and remove it.
316
315
317
316
Like with enums, the attribute is essentially ignored in the crate that defines
318
317
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.
321
321
322
- For example, using [ ` syn::Generics ` ] again:
322
+ For example, using our ` Config ` again:
323
323
324
324
``` rust
325
325
#[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 ,
330
329
}
331
330
```
332
331
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:
336
357
337
358
``` rust
338
- let val = Generics {
339
- lifetimes : Vec :: new (),
359
+ let val = Config {
360
+ window_width : 640 ,
361
+ window_height : 480 ,
340
362
.. Default :: default ()
341
363
};
342
364
```
343
365
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 ` ).
345
368
346
369
Although it should not be explicitly forbidden by the language to mark a struct
347
370
with some private fields as non-exhaustive, it should emit a warning to tell the
@@ -455,6 +478,5 @@ implemented downstream.
455
478
[ RFC 757 ] : https://github.com/rust-lang/rfcs/pull/757
456
479
[ `std::io::ErrorKind` ] : https://doc.rust-lang.org/1.17.0/std/io/enum.ErrorKind.html
457
480
[ `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
459
481
[ use clauses ] : https://github.com/rust-lang/rfcs/pull/1976#issuecomment-301903528
460
482
[ `byteorder` ] : https://github.com/BurntSushi/byteorder/tree/f8e7685b3a81c52f5448fd77fb4e0535bc92f880
0 commit comments