Skip to content

Commit 38cd2e4

Browse files
committed
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.)
1 parent 6918bcc commit 38cd2e4

File tree

1 file changed

+11
-11
lines changed

1 file changed

+11
-11
lines changed

text/3498-lifetime-capture-rules-2024.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ In return position `impl Trait` (RPIT) and `async fn`, an **opaque type** is a t
2424
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]
2525

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

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

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

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

7070
```rust
71-
async fn foo<'a, T>(x: &'a (), y: T) -> (&'a (), T) {
72-
(x, y)
71+
async fn foo<'a, T>(x: &'a (), y: T) {
72+
_ = (x, y);
7373
}
7474
```
7575

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

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

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

0 commit comments

Comments
 (0)