You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If we call `func(None)` then the type of `P` cannot be inferred. This is frustrating as in this case neither the caller nor the callee care about the type of `P`, any type would do. Default parameters allow the callee to choose a suitable default, making optional generic arguments ergonomically viable.
70
70
71
-
We may guess this is the most often occurring use case for default arguments. [It](https://github.com/PistonDevelopers/conrod/pull/626)[comes](https://github.com/gtk-rs/gir/issues/143)[up](https://github.com/jwilm/json-request/issues/1)[a lot](https://github.com/rust-lang/rust/issues/24857). We need type parameters defaults for optional arguments to be a well supported pattern, and even more so of we wish to dream of having optional arguments as a first-class language feature.
71
+
We may guess this is the most often occurring use case for default arguments. [Itcomes](https://github.com/gtk-rs/gir/issues/143)[up](https://github.com/jwilm/json-request/issues/1)[a lot](https://github.com/rust-lang/rust/issues/24857). We need type parameters defaults for optional arguments to be a well supported pattern, and even more so of we wish to dream of having optional arguments as a first-class language feature.
72
72
73
73
## Backwards-compatibily extending existing types
74
74
@@ -168,7 +168,7 @@ This would currently result in inference failures when trying to do `assert_ne!(
168
168
169
169
## Other motivations
170
170
171
-
- Helping type inference with too many candidate types. The famous case here is `Iterator::collect`. It is a common cause of turbofishes and type annotations because so many types implement `FromIterator`. But most of those types are niche and in the common case people just want a `Vec`. It would be nice if we could default `collect` to return a `Vec<Iterator::Item>`. Unfortunately we can't because `Iterator` is defined in `core` and `Vec` is defined in `std`. Perhaps there are similar use cases in the ecosystem.
171
+
- Helping type inference with too many candidate types. The famous case here is `Iterator::collect`. It is a common cause of turbofishes and type annotations because so many types implement `FromIterator`. But most of those types are niche and in the common case people just want a `Vec`. It would be nice if we could default `collect` to return a `Vec<Iterator::Item>`. Unfortunately we can't because `Iterator` is defined in `core` and `Vec` is defined in `std`, but maybe after the portability reform we can hack around this and perhaps there are similar use cases in the ecosystem.
172
172
173
173
- Making an already generic parameter more generic, for example the case of [generalizing `slice::contains` over `PartialEq`](https://github.com/rust-lang/rust/pull/46934).
174
174
@@ -289,7 +289,7 @@ The behaviour of partially supplied parameter lists is as per RFC 213, omited pa
289
289
290
290
A key part of this proposal is that inference is now aware of defaults. When we would otherwise error due to an uninferred type we instead try using the default. This is called inference fallback which is our final attempt at inference. The algorithm for doing this is essentialy the one detailed in RFC 213, with a few considerations:
291
291
292
-
- The interaction with literal fallback may change, see "Unresolved Questions".
292
+
- The interaction with numeric literals is different, see "Interaction with numerical fallback".
293
293
294
294
- The algorithm did not specify what happens in eager type resolution such as the `structurally_resolve_type` method, notably used to resolve method receivers. To prevent being a hazard to a future where no longer need to eagerly resolve types, we specify that eager type resolution will not do fallback.
295
295
@@ -332,6 +332,53 @@ The following things may cause inference breakage:
332
332
- Adding a default to an existing type parameter may break inference because it might cause conflicts among defaults, though that should be rare in practice. If an elided default is used the risk should be even smaller.
333
333
- Removing a default may of course break inference.
334
334
335
+
### Interaction with numerical fallback
336
+
337
+
There are multiple alternatives of what to do about the interaction of user fallback with numerical (and diverging) fallback. This was discussed at lenght in [this internals thread](https://internals.rust-lang.org/t/interaction-of-user-defined-and-integral-fallbacks-with-inference/2496). The possible target scenarios are:
338
+
339
+
1. Numerical fallback takes precedence, always.
340
+
2. User fallback takes precedence over numerical fallback, always.
341
+
3. DWIM: User fallback takes preference, but if it fails we try numerical fallback.
342
+
343
+
This RFC proposes that we go with option 3 (DWIM). The two following examples show the consequences of each alternative, example 1:
344
+
345
+
```rust
346
+
fnfoo<T=u64>(t:T) { ... }
347
+
// 1. `_` is `i32`
348
+
// 2. `_` is `u64`
349
+
// 3. `_` is `u64`
350
+
fnmain() { foo::<_>(22) }
351
+
```
352
+
353
+
Example 2:
354
+
355
+
```rust
356
+
fnfoo<T=char>(t:T) { ... }
357
+
// 1. `_` is `i32`
358
+
// 2. Error.
359
+
// 3. `_` is `i32`
360
+
fnmain() { foo::<_>(22) }
361
+
```
362
+
363
+
Option 3 gives the best results, but it would change the behaviour of (possibly) existing code such as:
364
+
365
+
```rust
366
+
structFoo<T=u64>(T);
367
+
// `_` was i32, now it's u64.
368
+
letx=Foo::<_>(0);
369
+
```
370
+
371
+
So if crater catches cases like this we will have to phase-in by keeping option 1 and introducing the new behaviour with lints.
372
+
373
+
One concern raised about DWIM is whether it would be a future-compatibility hazard for the possibility of expanding to which types literals can be inferred. To illustrate:
374
+
375
+
```rust
376
+
fnfoo<T=BigInt>(t:T) { ... }
377
+
fnmain() { foo::<_>(22) }
378
+
```
379
+
380
+
Currently DWIM would consider `_` to be `i32`, but in a future where literals may somehow be inferred to `BigInt`, what happens? We avoid that hazard by specifying that DWIM will prefer the user default only if it is one of the primitive numerics that work today. If/when we get this future feature, then we may decide whether to transition DWIM to also prefer non-primitive user defaults such as `BigInt` or not.
381
+
335
382
## Default elision
336
383
337
384
Default elision is the syntax `T = _` which indicates that the default is being taken from the type or trait definitions in which `T` is used. When default elision may be used for a parameter `T` but no default is set a lint is emitted to suggest writing `T =_`.
- Is there a better name for default elision? Default propagation? Default inheritance? Is there a better syntax than `A=_`?
517
+
469
518
- We could stabilize writing defaults in fns and impls first (without any effect) and later stabilize inference fallback, so that existing stable libraries have a period to adapt without being concerned of creating conflicts among defaults.
470
519
471
520
## Default elision
@@ -543,51 +592,6 @@ Soon we would be talking about things like syntax to promise a parameter has no
543
592
544
593
# Unresolved questions
545
594
546
-
The following unresolved questions should be resolved prior to stabilization, but hopefully shouldn't block the acceptance of the proposal:
547
-
548
-
### Interaction with numerical fallback
549
-
550
-
There are multiple alternatives of what to do about the interaction of user fallback with numerical (and diverging) fallback. This was discussed at lenght in [this internals thread](https://internals.rust-lang.org/t/interaction-of-user-defined-and-integral-fallbacks-with-inference/2496). The options were:
551
-
552
-
1. User fallback takes precedence over numerical fallback, always.
553
-
2. Numerical fallback takes precedence, always.
554
-
3. DWIM: User fallback takes preference, but if it fails we try numerical fallback.
555
-
4. Error on any ambiguity.
556
-
557
-
And now a fifth option proposed by this RFC:
558
-
559
-
5. Error on conflicting numericals, whenever DWIM would prefer a user fallback we instead error.
560
-
561
-
The two following examples show the consequences of each alternative, example 1:
562
-
563
-
```rust
564
-
fnfoo<T=u64>(t:T) { ... }
565
-
// 1. `_` is `u64`
566
-
// 2. `_` is `i32`
567
-
// 3. `_` is `u64`
568
-
// 4. Error.
569
-
// 5. Error.
570
-
fnmain() { foo::<_>(22) }
571
-
```
572
-
573
-
Example 2:
574
-
575
-
```rust
576
-
fnfoo<T=char>(t:T) { ... }
577
-
// 1. Error.
578
-
// 2. `_` is `i32`
579
-
// 3. `_` is `i32`
580
-
// 4. Error.
581
-
// 5. `_` is `i32`.
582
-
fnmain() { foo::<_>(22) }
583
-
```
584
-
585
-
Option 3 gives the best results, but it may change the behaviour of existing code so it might have to be phased-in through the errors given by option 4 or 5. The consensus reached in the thread was for using option 4 to open the possibility of transitioning to 3, is that still a consensus? However option 4 seems overly restrictive, we could instead do option 5 for a smoother transition.
586
-
587
-
### Terminology and syntax
588
-
589
-
Is there a better name for default elision? Default propagation? Default inheritance? Is there a better syntax than `A=_`?
590
-
591
595
### Interaction with specialization
592
596
593
597
Consider the example that shows the behaviour of the current implemetation:
@@ -618,4 +622,4 @@ fn main() {
618
622
}
619
623
```
620
624
621
-
We need to figure the design and implementation of defaults in specialization chains. Probably we want to allow only one default for a parameter in a specialization chain.
625
+
We need to figure the design and implementation of defaults in specialization chains. Probably we want to allow only one default for a parameter in a specialization chain. This needs to be resolved prior to stabilization, but hopefully shouldn't block the acceptance of the proposal.
0 commit comments