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 Appendix H with more exposition of outlives rules
The outlives rules related to RPIT-like `impl Trait` opaque types can
be a bit subtle. Even though this is only non-normative background
information, let's add an appendix that explains these rules less
concisely than in the main body and provides examples.
Copy file name to clipboardExpand all lines: text/3498-lifetime-capture-rules-2024.md
+101Lines changed: 101 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,8 @@ For an opaque type that *does not* specify an outlives bound (e.g. `+ 'other`),
34
34
35
35
For an opaque type that *does* specify an outlives bound (e.g. `+ 'other`), when a caller receives a value of that opaque type and wants to prove that it outlives some lifetime, it's enough to prove that the lifetime substituted for the specified lifetime parameter in the bounds of the opaque outlives that other lifetime after transitively taking into account all known lifetime bounds. For such an opaque type, the *callee* must prove that all lifetime and type parameters that are used in the hidden type outlive the specified bound.
36
36
37
+
See [Appendix H] for examples and further exposition of these rules.
38
+
37
39
## Capturing lifetimes in type parameters
38
40
39
41
In return position `impl Trait` (RPIT) and `async fn`, lifetimes contained within all in-scope type parameters are captured in the opaque type. For example:
Future work may relax this current limitation of the compiler. The result of such an improvement would be that, when an outlives bound is specified for the opaque type, any type or lifetime parameters that the compiler could prove to not outlive that bound would act as if they were not captured by the opaque type.
645
+
646
+
# Appendix H: Examples of outlives rules on opaque types
There is some subtlety in understanding the rules for outlives relationships on RPIT-like `impl Trait` opaque types as [described above](#capturing-lifetimes). In this appendix, we provide annotated examples to make these rules more clear.
651
+
652
+
### Caller proof for opaque without a specified bound
653
+
654
+
Consider:
655
+
656
+
```rust
657
+
// For an opaque type that *does not* specify an outlives bound...
// ...when a caller receives a value of that opaque type...
663
+
letz=callee(x, y);
664
+
// ...and wants to prove that it outlives some lifetime
665
+
// (`'short`), the caller must prove that all of the captured
666
+
// lifetime components of the opaque type (the lifetimes within
667
+
// `T` and `U`) outlive that lifetime (`'short`).
668
+
//
669
+
// The caller proves this because `T: 'short, U: 'short`.
670
+
outlives::<'short>(z);
671
+
}
672
+
```
673
+
674
+
In this example, the caller wants to prove that the returned opaque type outlives the lifetime `'short`. To prove this, since there is no specified outlives bound on the opaque type, it must prove that all lifetimes captured by the opaque type outlive `'short`. To do that, it must prove that `T` and `U` outlive `'short`, since those type parameters are captured by the opaque type and may contain lifetimes. The caller is able to prove this since `T: 'short, U: 'short`.
675
+
676
+
### Caller proof for opaque with a specified bound
677
+
678
+
Consider:
679
+
680
+
```rust
681
+
// For an opaque type that *does* specify an outlives bound...
682
+
fncallee<'o, T, U>(_:T, _:U) ->implSend+ 'o {}
683
+
684
+
fncaller<'short, 'long: 'short, T, U>(x:T, y:U) {
685
+
fnoutlives<'o, T: 'o>(_:T) {}
686
+
// ...when a caller receives a value of that opaque type...
687
+
letz=callee::<'long, _, _>(x, y);
688
+
// ...and wants to prove that it outlives some lifetime
689
+
// (`'short`), it's enough to prove that the lifetime substituted
690
+
// (`'long`) for the specified lifetime parameter (`'o` in
691
+
// `callee`) in the bounds of the opaque type outlives that other
692
+
// lifetime (`'short`).
693
+
//
694
+
// The caller proves this because `'long: 'short`.
695
+
outlives::<'short>(z);
696
+
}
697
+
```
698
+
699
+
In this example, the caller wants to prove that the returned opaque type outlives the lifetime `'short`. To prove this, since there is a specified outlives bound on the opaque type (`+ 'o` in `callee`), it must prove only that the lifetime substituted for that lifetime parameter outlives `'short`. Since `'long` is substituted for `'o`, and since `'long: 'short`, the caller is able to prove this. Note that the caller does *not* need to prove that `T: 'short` or that `U: 'short`.
700
+
701
+
### Callee proof for opaque with a specified bound
702
+
703
+
Consider:
704
+
705
+
```rust
706
+
// For an opaque type that *does* specify an outlives bound, the
707
+
// callee must prove that all lifetime and type parameters that are
708
+
// used in the hidden type (`T` in this example) outlive the specified
709
+
// bound (`'o`).
710
+
fncallee<'o, T: 'o, U>(x:T, _:U) ->implSized+ 'o { x }
711
+
```
712
+
713
+
In this example, the callee has specified an outlives bound on the opaque type (`+ 'o`). For this code to be valid, the callee must prove that all lifetime and type parameters used in the returned *hidden* type (`T` in this example) outlive `'o`. Since `T: 'o`, the callee is able to prove this. Note that even though `U` is also captured in the opaque type, the callee does *not* need to prove `U: 'o` since it is not used in the hidden type.
714
+
715
+
### Rough equivalence between opaques with and without a specified bound
In the first example, to prove that the opaque type outlives `'short`, the *caller* has to prove that each of the captured lifetime components outlives `'short`. In the second example, to prove that same thing, it only needs to prove that `'long: 'short`.
740
+
741
+
(Obviously, the caller then still needs to prove the outlives relationships necessary to satisfy the other specified bounds in the signature of `callee`.)
742
+
743
+
That is, at the cost of an extra early-bound lifetime parameter in the signature of the callee, we can always express an RPIT without a specified outlives bound as an RPIT with a specified outlives bound in a way that does not change the requirements on the caller or the callee. We do this by transforming a signature of the form `fn callee<P1, .., Pn>(..) -> impl Trait` to a signature of the form `fn callee<'o, P1: 'o, .., Pn: 'o>(..) -> impl Trait + 'o`.
0 commit comments