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
Add clarifications on refinement, reparameterization
During the FCP, some questions came up related to how refinement and
reparameterization in the impl are handled. This handling is implied
by other text in the RFC and by the existing behavior of Rust, so
let's go ahead and add clarifications to address these questions.
The hardest of these questions relate to how things would behave if we
were to allow `use<..>` in trait definitions to not capture the
generic input parameters to the trait (including `Self`). It's
unlikely this will be possible for the foreseeable future, and while
we will not leave these as open questions, certainly much might be
learned between now and the point at which that might become possible,
so we'll make note of that.
We'll also add a clarification to address a question that came up in
the 2024-04-24 design meeting about what it means to capture a const
generic parameter.
(Thanks to aliemjay for raising many of these great questions.)
Copy file name to clipboardExpand all lines: text/3617-precise-capturing.md
+185-4Lines changed: 185 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -377,7 +377,7 @@ impl Trait for B {
377
377
}
378
378
```
379
379
380
-
If we only know that the value is of some type that implements the trait, then we must assume that the type returned by `foo`*might*have used the lifetime:
380
+
If we only know that the value is of some type that implements the trait, then we must assume that the type returned by `foo`*might*use the lifetime:
However, if we know we have a value of type `B`, we can *rely* on the fact that the lifetime was not used:
389
+
However, if we know we have a value of type `B`, we can *rely* on the fact that the lifetime is not used:
390
390
391
391
```rust
392
392
fntest_b(x:B) ->implSized+ 'static {
@@ -415,11 +415,15 @@ impl Trait for () {
415
415
}
416
416
```
417
417
418
-
Similarly, for consistency, we'll lint against RPITIT cases where less is captured by RPIT in the impl as compared with the trait definition when using `use<..>`. E.g.:
418
+
Similarly, for consistency, we'll lint against RPITIT cases where less is captured by RPIT in the impl as compared with the trait definition when using `use<..>`.
419
+
420
+
### Examples of refinement
421
+
422
+
In keeping with the rule above, we consider it refining if we don't capture in the impl all of the generic parameters from the function signature that are captured in the trait definition:
Similarly, if we don't capture, in the impl, any generic parameter applied as an argument to the trait in the impl header when the corresponding generic parameter is captured in the trait definition, that is refining. E.g.:
This remains true even if the trait impl is *reparameterized*. In that case, it is refining unless *all* generic parameters applied in the impl header as generic arguments for the corresponding trait parameter are captured in the impl when that parameter is captured in the trait definition, e.g.:
Similarly, it's refining if `Self` is captured in the trait definition and, in the impl, we don't capture all of the generic parameters that are applied in the impl header as generic arguments to the `Self` type, e.g.:
480
+
481
+
```rust
482
+
traitTrait {
483
+
fnf() ->implSized; // Or: `impl use<Self> Sized`
484
+
}
485
+
486
+
structS<T>(T);
487
+
impl<'a, 'b> TraitforS<(&'a (), &'b ())> {
488
+
fnf() ->impluse<'b> Sized {}
489
+
//~^ WARN impl trait in impl method signature does not match
490
+
//~| trait method signature
491
+
//~| NOTE add `#[allow(refining_impl_trait)]` if it is intended
492
+
//~| for this to be part of the public API of this crate
493
+
//~| NOTE we are soliciting feedback, see issue #121718
While the capturing of generic parameters is generally syntactic, this is currently allowed in Rust 2021:
502
+
503
+
```rust
504
+
//@ edition: 2021
505
+
fnfoo<'a: 'b, 'b: 'a>() ->implSized+ 'b {
506
+
core::marker::PhantomData::<&'a ()>
507
+
}
508
+
```
509
+
510
+
Rust 2021 does not adhere to the Lifetime Capture Rules 2024 for bare RPITs such as this. Correspondingly, lifetimes are only captured when they appear in the bounds. Here, `'b` but not `'a` appears in the bounds, yet we're still able to capture `'a` due to the fact that it must be equal to `'b`.
511
+
512
+
To preserve consistency with this, the following is also valid:
513
+
514
+
515
+
```rust
516
+
fnfoo<'a: 'b, 'b: 'a>() ->impluse<'b> Sized {
517
+
core::marker::PhantomData::<&'a ()>
518
+
}
519
+
```
520
+
521
+
A more difficult case is where, in the trait definition, only a subset of the generic parameters on the trait are captured, and in the impl we capture a lifetime *not* applied syntactically as an argument for one of those captured parameters but which is equal to a lifetime that is applied as an argument for one of the captured parameters, e.g.:
522
+
523
+
```rust
524
+
traitTrait<'x, 'y> {
525
+
fnf() ->impluse<'y, Self> Sized;
526
+
}
527
+
528
+
impl<'a: 'b, 'b: 'a> Trait<'a, 'b> for () {
529
+
fnf() ->impluse<'b> Sized {
530
+
core::marker::PhantomData::<&'a ()>
531
+
}
532
+
}
533
+
```
534
+
535
+
For the purposes of this RFC, in the interest of consistency with the above cases, we're going to say that this is valid. However, as mentioned elsewhere, partial capturing of generics that are input parameters to the trait (including `Self`) is unlikely to be part of initial rounds of stabilization, and it's possible that implementation experience may lead us to a different answer for this case.
536
+
537
+
## Reparameterization
538
+
539
+
In Rust, trait impls may be parameterized over a different set of generics than the trait itself. E.g.:
In these cases, what we look at is how these generics are applied as arguments to the trait in the impl header. In this example, all of `'a`, `B`, and `C` are applied in place of the `Y` input parameter to the trait. Since `Y` is captured in the trait definition, we're correspondingly allowed to capture `'a`, `B`, and `C` in the impl.
554
+
555
+
## The `Self` type
556
+
557
+
In trait definitions (but not elsewhere), `use<..>` may capture `Self`. Doing so means that in the impl, the opaque type may capture any generic parameters that are applied as generic arguments to the `Self` type. E.g.:
If we apply, in a trait impl header, a projection type to a trait in place of a parameter that is captured in the trait definition, that does not allow us to capture in the impl the generic parameter from which the type is projected. E.g.:
575
+
576
+
```rust
577
+
traitTrait<X, Y> {
578
+
fnf() ->impluse<Y, Self> Sized;
579
+
}
580
+
581
+
impl<A:Iterator> Trait<A, A::Item> for () {
582
+
fnf() ->impluse<A> Sized {}
583
+
//~^ ERROR cannot capture `A`
584
+
}
585
+
```
586
+
587
+
The reason this is an error is related to the fact that, in Rust, a generic parameter used as an associated type does not constrain that generic parameter in the impl. E.g.:
588
+
589
+
```rust
590
+
traitTrait {
591
+
typeTy;
592
+
}
593
+
594
+
impl<A> Traitfor () {
595
+
//~^ ERROR the type parameter `A` is not constrained
596
+
typeTy=A;
597
+
}
598
+
```
599
+
600
+
## Meaning of capturing a const generic parameter
601
+
602
+
As with other generic parameters, a const generic parameter must be captured in the opaque type for it to be used in the hidden *type*. E.g., we must capture `C` here:
603
+
604
+
```rust
605
+
fnf<constC:usize>() ->impluse<C> Sized {
606
+
[(); C]
607
+
}
608
+
```
609
+
610
+
However, note that we do not need to capture `C` just to use it as a *value*, e.g.:
611
+
612
+
```rust
613
+
fnf<constC:usize>() ->impluse<> Sized {
614
+
C+1
615
+
}
616
+
```
617
+
437
618
## Argument position impl Trait
438
619
439
620
Note that for a generic type parameter to be captured with `use<..>` it must have a name. Anonymous generic type parameters introduced with argument position `impl Trait` (APIT) syntax don't have names, and so cannot be captured with `use<..>`. E.g.:
0 commit comments