@@ -187,8 +187,8 @@ $struct_name:path {
187
187
```
188
188
189
189
Struct update syntax is directly equivalent to explicitly listing all of the
190
- fields, with the exception of type inference and type checking, as described in
191
- the following section. For example, the listing from the previous section
190
+ fields, with the possible exception of type inference. For example, the listing
191
+ from the previous section
192
192
193
193
``` rust
194
194
let logged_in = User {
@@ -207,340 +207,22 @@ let logged_in = User {
207
207
};
208
208
```
209
209
210
- except for type inference and type checking.
211
-
212
- ## Type inference with struct update syntax
213
-
214
- Given an instance of struct update syntax:
215
-
216
- ``` rust
217
- let updated = Struct {
218
- field1 : expr1 ,
219
- field2 : expr2 ,
220
- .. base
221
- };
222
- ```
223
-
224
- it is known that ` updated ` and ` base ` must be instances of ` Struct ` , but the
225
- generic type parameters and lifetimes of ` Struct ` may need to be inferred for
226
- ` updated ` and ` base ` . Note that in current Rust, the outer struct type
227
- (` Struct ` in this example) is always known, even if the identifier ` Struct ` is
228
- replaced with a type alias or ` Self ` , but this may change if [ RFC 2515] gets
229
- merged.
230
-
231
- Type inference of the generic type parameters and lifetimes will follow these
232
- rules:
233
-
234
- 1 . If the type of ` updated ` can be a subtype of the type of ` base ` without
235
- violating any constraints, then the type of ` updated ` is inferred to be a
236
- subtype of the type of ` base ` . This rule is evaluated simultaneously for all
237
- instances of struct update syntax within the inference context, and
238
- conflicts between applications of this rule should result in compilation
239
- errors.
240
-
241
- 2 . If the type of ` updated ` cannot be a subtype of the type of ` base ` without
242
- violating any constraints, then the explicitly listed fields (` field1 ` and
243
- ` field2 ` in the example) are inferred independently between ` updated ` and
244
- ` base ` . In other words, in this case, the example can be equivalently
245
- expanded into the following:
246
-
247
- ``` rust
248
- let updated = Struct {
249
- field1 : expr1 ,
250
- field2 : expr2 ,
251
- kept_field1 : (result of base ). kept_field1,
252
- kept_field2 : (result of base ). kept_field2,
253
- kept_field3 : (result of base ). kept_field3,
254
- };
255
- ```
256
-
257
- These rules preserve the inferred types of existing Rust code while minimizing
258
- the assumptions type inference makes for the type-changing case.
259
-
260
- For example, the inferred type of ` updated ` is ` Foo<u8> ` in the following
261
- example in both current Rust and with this RFC:
262
-
263
- ``` rust
264
- struct Foo <A > {
265
- a : A ,
266
- b : & 'static str ,
267
- }
268
-
269
- let base : Foo <u8 > = Foo {
270
- a : 1 ,
271
- b : " hello" ,
272
- };
273
- let updated = Foo {
274
- a : 2 ,
275
- .. base
276
- };
277
- ```
278
-
279
- Since the type of ` updated ` can be a subtype of the type of ` base ` without
280
- violating any constraints, it is inferred to be so. Note, in particular, that
281
- this rule takes precedence over the Rust fallback integer type ` i32 ` for the
282
- ` 2 ` literal.
283
-
284
- If the type of ` updated ` cannot be a subtype of the type of ` base ` without
285
- violating any constraints, then inference for the explicitly listed fields is
286
- handled independently between the base and updated instances. For example:
287
-
288
- ``` rust
289
- struct Foo <A , B > {
290
- a : A ,
291
- b : B ,
292
- c : i32 ,
293
- }
294
-
295
- let base = Foo {
296
- a : 1u8 ,
297
- b : 2u8 ,
298
- c : 3i32 ,
299
- };
300
- let updated = Foo {
301
- a : " hello" ,
302
- b : 2 ,
303
- .. base
304
- }
305
- ```
306
-
307
- In this case ` base ` has type ` Foo<u8, u8> ` . Since the type of ` updated ` cannot
308
- be a subtype of the type of ` base ` due to the change in the type of the ` a `
309
- field, the types of ` base.b ` and ` updated.b ` are inferred independently. Since
310
- ` updated.b ` is an unconstrained integer literal, it has the Rust integer
311
- fallback type ` i32 ` , and so ` updated ` is inferred to have type `Foo<&'static
312
- str, i32>`.
313
-
314
- The same behavior can also be caused by a broadening in a lifetime, since if
315
- ` updated ` needs a broader lifetime than ` base ` , its type cannot be a subtype of
316
- the type of `base. For example:
317
-
318
- ``` rust
319
- struct Foo <'a , B > {
320
- a : & 'a (),
321
- b : B ,
322
- c : i32 ,
323
- }
324
-
325
- let tup_stack = ();
326
- let base = Foo {
327
- a : & tup_stack ,
328
- b : 2u8 ,
329
- c : 3i32 ,
330
- };
331
- let tup_static : & 'static () = & ();
332
- let updated : Foo <'static , _ > = Foo {
333
- a : tup_static ,
334
- b : 2 ,
335
- .. base
336
- };
337
- ```
338
-
339
- Since the lifetime of ` &tup_stack ` is shorter than the ` 'static ` lifetime, the
340
- type of ` updated ` cannot be a subtype of the type of ` base ` . As a result, the
341
- types of ` base.b ` and ` updated.b ` are inferred independently. Since ` updated.b `
342
- is an unconstrained integer literal, it has the Rust integer fallback type
343
- ` i32 ` , and so ` updated ` is inferred to have type ` Foo<'static, i32> ` .
344
-
345
- There is an edge case to rule 1 when there are multiple instances of struct
346
- update syntax within a single inference context. For example:
347
-
348
- ``` rust
349
- struct Foo <A > {
350
- a : A ,
351
- b : & 'static str ,
352
- }
353
-
354
- let base_u8 : Foo <u8 > = Foo {
355
- a : 1 ,
356
- b : " hello" ,
357
- };
358
- let base_u16 : Foo <u16 > = Foo {
359
- a : 1 ,
360
- b : " hello" ,
361
- };
362
-
363
- let a_unknown_int = 2 ;
364
- let updated1 = Foo {
365
- a : a_unknown_int ,
366
- .. base_u8
367
- };
368
- let updated2 = Foo {
369
- a : a_unknown_int ,
370
- .. base_u16
371
- };
372
- ```
373
-
374
- Individually, the types of ` updated1 ` and ` updated2 ` can be subtypes of the
375
- types of ` base_u8 ` and ` base_u16 ` , respectively. However, ` updated1.a ` and
376
- ` updated2.a ` must have the same type because they are copies of
377
- ` a_unknown_int ` . As a result, there is a conflict with both ` updated1 ` and
378
- ` updated2 ` being subtypes of ` base_u8 ` and ` base_u16 ` , respectively, so a
379
- compilation error should be generated.
210
+ except, possibly, for type inference.
380
211
381
212
# Drawbacks
382
213
[ drawbacks ] : #drawbacks
383
214
384
- If the user does not know the type inference rules described earlier, type
385
- inference may result in slightly surprising results for struct update syntax
386
- with numeric literals. For example:
387
-
388
- ``` rust
389
- struct Foo <A , B > {
390
- a : A ,
391
- b : B ,
392
- c : i32 ,
393
- }
394
-
395
- let base = Foo {
396
- a : 1u8 ,
397
- b : 2u8 ,
398
- c : 3i32 ,
399
- };
400
- let updated = Foo {
401
- a : " hello" ,
402
- b : 2 ,
403
- .. base
404
- }
405
- ```
406
-
407
- In this case ` base ` has type ` Foo<u8, u8> ` , while ` updated ` is inferred to have
408
- type ` Foo<&'static str, i32> ` . If users are not aware that changing the type of
409
- ` a ` results in inferring ` b ` independently for ` base ` and ` updated ` , they may
410
- be surprised that the type of ` b ` has changed from ` u8 ` to ` i32 ` . The same
411
- behavior can also result from broadening lifetimes between ` base ` and
412
- ` updated ` , which may be especially surprising because lifetimes are usually
413
- implicit in Rust.
215
+ There are trade-offs to be made when selecting the type inference strategy,
216
+ since the types of fields are no longer necessarily the same between the base
217
+ and updated instances in struct update syntax. See the * Type inference* section
218
+ under [ Unresolved questions] ( #unresolved-questions ) .
414
219
415
220
# Rationale and alternatives
416
221
[ rationale-and-alternatives ] : #rationale-and-alternatives
417
222
418
223
This proposal is a relatively small user-facing generalization that
419
224
significantly improves language ergonomics in some cases.
420
225
421
- ## Alternative type inference rules
422
-
423
- A variety of alternative type inference rules are possible.
424
-
425
- ### Initially assume that individual fields do not change type
426
-
427
- The inference rule proposed in this RFC means that if the type of * any* of the
428
- fields changes, the types of * all* other explicitly listed fields are inferred
429
- independently between the base and updated instances. Alternatively, type
430
- inference could treat fields individually, assuming that the type of each
431
- individual field is the same between the base and updated instances unless it
432
- violates a constraint, regardless of the types of the other fields. Given this
433
- example:
434
-
435
- ``` rust
436
- struct Foo <A , B > {
437
- a : A ,
438
- b : B ,
439
- c : i32 ,
440
- }
441
-
442
- let base = Foo {
443
- a : 1u8 ,
444
- b : 2u8 ,
445
- c : 3i32 ,
446
- };
447
- let updated = Foo {
448
- a : " hello" ,
449
- b : 2 ,
450
- .. base
451
- }
452
- ```
453
-
454
- the approach proposed in the RFC infers the type of ` updated ` to be
455
- ` Foo<&'static str, i32> ` , while this alternative type inference rule infers the
456
- type of ` updated ` to be ` Foo<&'static str, u8> ` because the type of ` b ` can be
457
- the same in both ` base ` and ` updated ` without violating any constraints.
458
-
459
- This alternative rule reduces the need for explicit type annotations in some
460
- cases, but it may also be surprising to users who expect explicitly listed
461
- fields to be inferred independently of the base instance.
462
-
463
- ### Disable ` i32 ` /` f64 ` fallback for explicitly listed fields
464
-
465
- When an integer or floating point literal is unconstrained in Rust, its type is
466
- inferred to be the fallback of ` i32 ` or ` f64 ` . This could be slightly
467
- surprising in combination with the type inference rules proposed in this RFC,
468
- as described in the [ Drawbacks] [ drawbacks ] section. One possibility is to
469
- disable the ` i32 ` /` f64 ` fallback for explicitly listed fields in struct update
470
- syntax, and instead throw a compilation error if the specific type of literal
471
- cannot be inferred. This may avoid confusion caused by the recommended proposal
472
- or the previously described alternative inference rule by throwing a
473
- compilation error whenever the type is not clear.
474
-
475
- ### Always independently infer types of explicitly listed fields
476
-
477
- The type inference rules proposed in this RFC assume that the type of the
478
- updated struct matches the type of the base struct unless this assumption
479
- violates any constraints. This preserves backwards compatibility with existing
480
- code but is a special case that the user needs to be aware of. An alternative
481
- approach that is more consistent with the rest of the language and doesn't
482
- require any special cases is to always infer the types of explicitly listed
483
- fields independently between the base and updated instances. This alternative
484
- approach can break existing code in two ways:
485
-
486
- 1 . It can require additional explicit type annotations in some cases. For
487
- example:
488
-
489
- ``` rust
490
- struct Foo <T > {
491
- a : Vec <T >,
492
- b : i32 ,
493
- }
494
-
495
- let base : Foo <u8 > = Foo {
496
- a : Vec :: new (),
497
- b : 5 ,
498
- };
499
- let updated = Foo {
500
- a : Vec :: new (),
501
- .. base
502
- };
503
- ```
504
-
505
- In current ` Rust ` , ` updated ` always has the same type as ` base ` , so no
506
- additional type annotations are necessary. With this alternative inference
507
- rule, the type of ` a ` in ` updated ` is inferred independently of the type of
508
- ` a ` in ` base ` , so it is ambiguous and an explicit type annotation is
509
- necessary.
510
-
511
- 2 . It can change the inferred type of a struct instance in existing Rust code.
512
- For example,
513
-
514
- ``` rust
515
- struct Foo <A > {
516
- a : A ,
517
- b : i32 ,
518
- }
519
-
520
- let base : Foo <u8 > = Foo {
521
- a : 1 ,
522
- b : 2 ,
523
- };
524
- let updated = Foo {
525
- a : 3 ,
526
- .. base
527
- };
528
- ```
529
-
530
- In current Rust, ` updated ` has type ` Foo<u8> ` . With this alternative
531
- inference rule, the type of ` a ` in ` updated ` is inferred independently of
532
- the type of ` a ` in ` base ` . Since it is an integer literal without any
533
- constraints, the type is inferred to be the Rust integer fallback ` i32 ` . So,
534
- with the alternative inference rule, the type of ` updated ` would be
535
- ` Foo<i32> ` instead of ` Foo<u8> ` as it is in current Rust.
536
-
537
- ### Combination of always independently inferring types of explicitly listed fields and disabling ` i32 ` /` f64 ` fallback
538
-
539
- A combination of the previous two alternatives would still be a breaking change
540
- by requiring additional type annotations in some cases, but it would not
541
- silently change inferred types in existing code. All breakage would result in
542
- easy-to-fix compile-time errors.
543
-
544
226
## Further generalization
545
227
546
228
This proposal maintains the restriction that the types of the base and updated
@@ -629,23 +311,21 @@ Error: This expression has type foo but an expression was expected of type
629
311
# Unresolved questions
630
312
[ unresolved-questions ] : #unresolved-questions
631
313
314
+ ## Type inference
315
+
316
+ What is the best type inference strategy? In today's Rust, the types of the
317
+ explicitly listed fields are always the same in the base and updated instances.
318
+ With this RFC, the types of the explicitly listed fields can be different
319
+ between the base and updated instances. This removes some of the constraints on
320
+ type inference compared to today's Rust. There are choices to make regarding
321
+ backwards compatibility of inferred types, the ` i32 ` /` f64 ` fallback in type
322
+ inference, and the conceptual simplicity of the chosen strategy.
323
+
324
+ ## Further generalization
325
+
632
326
Should struct update syntax be further generalized to ignore the struct type
633
327
and just consider field names and field types? This question could be answered
634
328
later after users have experience with the changes this RFC. The further
635
329
generalization could be implemented in a backwards-compatible way.
636
330
637
- What is the best inference rule? The proposal tries to strike a balance between
638
- consistency, simplicity, and backwards compatibility, but one of the
639
- alternative inference rules may be preferable.
640
-
641
- Is the proposed inference rule practical to implement? With this rule, region
642
- constraints can affect the inferred type, since checking if the type of
643
- ` updated ` can be a subtype of the type of ` base ` requires checking region
644
- constraints. This may be problematic since type inference is currently
645
- implemented in separate phases for non-region and region constraints. One
646
- possible workaround for this is to always infer lifetimes in explicitly listed
647
- fields independently between ` updated ` and ` base ` , but would this be backwards
648
- compatible?
649
-
650
331
[ RFC 736 ] : https://github.com/rust-lang/rfcs/blob/master/text/0736-privacy-respecting-fru.md
651
- [ RFC 2515 ] : https://github.com/rust-lang/rfcs/pull/2515
0 commit comments