Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifetime Capture Rules 2024 #3498

Merged
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Improve async fn capturing examples
What we want to show with `async fn` is that it automatically captures
all in-scope type and lifetime parameters in the returned opaque
`Future`.  We're doing this to contrast it with RPIT in Rust 2021 and
earlier editions which does not capture in the returned opaque type
all in-scope lifetime parameters automatically.

However, our examples did not well demonstrate this, because the
examples used the lifetime parameters in the `async fn` return types,
which results in those lifetime parameters appearing in the associated
type of each returned opaque `Future`.  In the RPIT desugarings, the
lifetime parameters would therefore appear in the bounds of the `impl
Trait` opaque types, and so would be captured regardless.

To better draw the distinction we want to draw, let's change each
`async fn` example to simply return the unit type (`()`).  This
ensures that in the RPIT desugarings any lifetime parameters will not
be automatically captured under the rules of Rust 2021 and earlier
editions.

For each example, we'll use each of the type and lifetime parameters
in the body of the function so that they will appear in the returned
hidden type.  This ensures that the RPIT desugarings for the examples
where a lifetime parameter is captured cannot be expressed without
using one of the tricks.

(Thanks to @tmandry for raising this important point.)
  • Loading branch information
traviscross committed Oct 5, 2023
commit 38cd2e492f0d1db3f31a4862ac369b3cc3a8f57b
22 changes: 11 additions & 11 deletions text/3498-lifetime-capture-rules-2024.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ In return position `impl Trait` (RPIT) and `async fn`, an **opaque type** is a t
A hidden type is only allowed to name lifetime parameters when those lifetime parameters have been *"captured"* by the corresponding opaque type. For example:[^ref-captures-trait-ltps]

```rust
// Returns: `Future<Output = &'a ()> + Captures<&'a ()>`
async fn foo<'a>(x: &'a ()) -> &'a () { x }
// Returns: `Future<Output = ()> + Captures<&'a ()>`
async fn foo<'a>(x: &'a ()) { _ = (x,); }
```

In the above, we would say that the lifetime parameter `'a` has been captured in the returned opaque type.
Expand All @@ -43,8 +43,8 @@ See [Appendix H] for examples and further exposition of these rules.
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:[^ref-captures-trait-tps]

```rust
// Returns: Future<Output = T> + Captures<T>
async fn foo<T>(x: T) -> T { x }
// Returns: Future<Output = ()> + Captures<T>
async fn foo<T>(x: T) { _ = (x,); }

fn bar<'a>(x: &'a ()) {
let y = foo(x);
Expand All @@ -68,8 +68,8 @@ The inconsistency is visible to users when desugaring from `async fn` to RPIT.
For example, given this `async fn`:

```rust
async fn foo<'a, T>(x: &'a (), y: T) -> (&'a (), T) {
(x, y)
async fn foo<'a, T>(x: &'a (), y: T) {
_ = (x, y);
}
```

Expand All @@ -82,10 +82,10 @@ trait Captures<U> {}
impl<T: ?Sized, U> Captures<U> for T {}

fn foo<'a, T>(x: &'a (), y: T)
-> impl Future<Output = (&'a (), T)> + Captures<&'a ()> {
// ^^^^^^^^^^^^^^^^
// ^ Capture of lifetime.
async move { (x, y) }
-> impl Future<Output = ()> + Captures<&'a ()> {
// ^^^^^^^^^^^^^^^^
// ^ Capture of lifetime.
async move { _ = (x, y); }
}
```

Expand All @@ -100,7 +100,7 @@ Lifetimes in scope from an outer impl are also captured automatically by an `asy
```rust
struct Foo<'a>(&'a ());
impl<'a> Foo<'a> {
async fn foo(x: &'a ()) {}
async fn foo(x: &'a ()) { _ = (x,); }
// ^^^^^^^^^^^^^^
// ^ The lifetime `'a` is automatically
// captured in the opaque return type.
Expand Down