Skip to content

Commit 183dd4b

Browse files
authored
Merge pull request #1692 from compiler-errors/async-closures
Describe async closures
2 parents d6d24b9 + 8eb71da commit 183dd4b

File tree

7 files changed

+107
-8
lines changed

7 files changed

+107
-8
lines changed

mdbook-spec/src/std_links.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ fn run_rustdoc(tmp: &TempDir, chapter_links: &HashMap<&PathBuf, Vec<Link<'_>>>)
247247
fs::write(&src_path, &src).unwrap();
248248
let rustdoc = std::env::var("RUSTDOC").unwrap_or_else(|_| "rustdoc".into());
249249
let output = Command::new(rustdoc)
250-
.arg("--edition=2021")
250+
.arg("--edition=2024")
251251
.arg(&src_path)
252252
.current_dir(tmp.path())
253253
.output()

src/expressions/await-expr.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ An `await` expression is a syntactic construct for suspending a computation
88
provided by an implementation of `std::future::IntoFuture` until the given
99
future is ready to produce a value.
1010
The syntax for an await expression is an expression with a type that implements the [`IntoFuture`] trait, called the *future operand*, then the token `.`, and then the `await` keyword.
11-
Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block].
11+
Await expressions are legal only within an [async context], like an [`async fn`], [`async` closure], or [`async` block].
1212

1313
More specifically, an await expression has the following effect.
1414

@@ -48,6 +48,7 @@ The variable `current_context` refers to the context taken from the async enviro
4848

4949
[_Expression_]: ../expressions.md
5050
[`async fn`]: ../items/functions.md#async-functions
51+
[`async` closure]: closure-expr.md#async-closures
5152
[`async` block]: block-expr.md#async-blocks
5253
[`Context`]: std::task::Context
5354
[`future::poll`]: std::future::Future::poll

src/expressions/call-expr.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
A *call expression* calls a function.
1111
The syntax of a call expression is an expression, called the *function operand*, followed by a parenthesized comma-separated list of expression, called the *argument operands*.
1212
If the function eventually returns, then the expression completes.
13-
For [non-function types], the expression `f(...)` uses the method on one of the [`std::ops::Fn`], [`std::ops::FnMut`] or [`std::ops::FnOnce`] traits, which differ in whether they take the type by reference, mutable reference, or take ownership respectively.
13+
14+
For [non-function types], the expression `f(...)` uses the method on one of the following traits based on the function operand:
15+
16+
- [`Fn`] or [`AsyncFn`] --- shared reference.
17+
- [`FnMut`] or [`AsyncFnMut`] --- mutable reference.
18+
- [`FnOnce`] or [`AsyncFnOnce`] --- value.
19+
1420
An automatic borrow will be taken if needed.
1521
The function operand will also be [automatically dereferenced] as required.
1622

src/expressions/closure-expr.md

+27-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
> **<sup>Syntax</sup>**\
44
> _ClosureExpression_ :\
5+
> &nbsp;&nbsp; `async`[^cl-async-edition]<sup>?</sup>\
56
> &nbsp;&nbsp; `move`<sup>?</sup>\
67
> &nbsp;&nbsp; ( `||` | `|` _ClosureParameters_<sup>?</sup> `|` )\
78
> &nbsp;&nbsp; ([_Expression_] | `->` [_TypeNoBounds_]&nbsp;[_BlockExpression_])
@@ -11,9 +12,11 @@
1112
>
1213
> _ClosureParam_ :\
1314
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup> [_PatternNoTopAlt_]&nbsp;( `:` [_Type_] )<sup>?</sup>
15+
>
16+
> [^cl-async-edition]: The `async` qualifier is not allowed in the 2015 edition.
1417
1518
A *closure expression*, also known as a lambda expression or a lambda, defines a [closure type] and evaluates to a value of that type.
16-
The syntax for a closure expression is an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], called the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, called the *return type*, and then an expression, called the *closure body operand*.
19+
The syntax for a closure expression is an optional `async` keyword, an optional `move` keyword, then a pipe-symbol-delimited (`|`) comma-separated list of [patterns], called the *closure parameters* each optionally followed by a `:` and a type, then an optional `->` and type, called the *return type*, and then an expression, called the *closure body operand*.
1720
The optional type after each pattern is a type annotation for the pattern.
1821
If there is a return type, the closure body must be a [block].
1922

@@ -29,10 +32,32 @@ This is often used to ensure that the closure's lifetime is `'static`.
2932

3033
## Closure trait implementations
3134

32-
Which traits the closure type implement depends on how variables are captured and the types of the captured variables.
35+
Which traits the closure type implement depends on how variables are captured, the types of the captured variables, and the presence of `async`.
3336
See the [call traits and coercions] chapter for how and when a closure implements `Fn`, `FnMut`, and `FnOnce`.
3437
The closure type implements [`Send`] and [`Sync`] if the type of every captured variable also implements the trait.
3538

39+
## Async closures
40+
41+
Closures marked with the `async` keyword indicate that they are asynchronous in an analogous way to an [async function][items.fn.async].
42+
43+
Calling the async closure does not perform any work, but instead evaluates to a value that implements [`Future`] that corresponds to the computation of the body of the closure.
44+
45+
```rust
46+
async fn takes_async_callback(f: impl AsyncFn(u64)) {
47+
f(0).await;
48+
f(1).await;
49+
}
50+
51+
async fn example() {
52+
takes_async_callback(async |i| {
53+
core::future::ready(i).await;
54+
println!("done with {i}.");
55+
}).await;
56+
}
57+
```
58+
59+
> **Edition differences**: Async closures are only available beginning with Rust 2018.
60+
3661
## Example
3762

3863
In this example, we define a function `ten_times` that takes a higher-order function argument, and we then call it with a closure expression as an argument, followed by a closure expression that moves values from its environment.

src/items/traits.md

+3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ r[items.traits.dyn-compatible.associated-functions]
120120
* Explicitly non-dispatchable functions require:
121121
* Have a `where Self: Sized` bound (receiver type of `Self` (i.e. `self`) implies this).
122122

123+
r[items.traits.dyn-compatible.async-traits]
124+
* The [`AsyncFn`], [`AsyncFnMut`], and [`AsyncFnOnce`] traits are not dyn-compatible.
125+
123126
> **Note**: This concept was formerly known as *object safety*.
124127
125128
```rust

src/types/closure.md

+66-2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ let c = || {
9494
};
9595
```
9696

97+
### Async input capture
98+
99+
r[type.closure.async.input]
100+
Async closures always capture all input arguments, regardless of whether or not they are used within the body.
101+
97102
## Capture Precision
98103

99104
r[type.closure.capture.precision.capture-path]
@@ -491,7 +496,7 @@ r[type.closure.call.fn]
491496
492497
r[type.closure.non-capturing]
493498
*Non-capturing closures* are closures that don't capture anything from their
494-
environment. They can be coerced to function pointers (e.g., `fn()`)
499+
environment. Non-async, non-capturing closures can be coerced to function pointers (e.g., `fn()`)
495500
with the matching signature.
496501

497502
```rust
@@ -504,7 +509,66 @@ let bo: Binop = add;
504509
x = bo(5,7);
505510
```
506511

507-
## Other traits
512+
### Async closure traits
513+
514+
r[type.closure.async.traits]
515+
516+
r[type.closure.async.traits.fn-family]
517+
Async closures have a further restriction of whether or not they implement [`FnMut`] or [`Fn`].
518+
519+
The [`Future`] returned by the async closure has similar capturing characteristics as a closure. It captures place expressions from the async closure based on how they are used. The async closure is said to be *lending* to its [`Future`] if it has either of the following properties:
520+
521+
- The `Future` includes a mutable capture.
522+
- The async closure captures by value, except when the value is accessed with a dereference projection.
523+
524+
If the async closure is lending to its `Future`, then [`FnMut`] and [`Fn`] are *not* implemented. [`FnOnce`] is always implemented.
525+
526+
> **Example**: The first clause for a mutable capture can be illustrated with the following:
527+
>
528+
> ```rust,compile_fail
529+
> fn takes_callback<Fut: Future>(c: impl FnMut() -> Fut) {}
530+
>
531+
> fn f() {
532+
> let mut x = 1i32;
533+
> let c = async || {
534+
> x = 2; // x captured with MutBorrow
535+
> };
536+
> takes_callback(c); // ERROR: async closure does not implement `FnMut`
537+
> }
538+
> ```
539+
>
540+
> The second clause for a regular value capture can be illustrated with the following:
541+
>
542+
> ```rust,compile_fail
543+
> fn takes_callback<Fut: Future>(c: impl Fn() -> Fut) {}
544+
>
545+
> fn f() {
546+
> let x = &1i32;
547+
> let c = async move || {
548+
> let a = x + 2; // x captured ByValue
549+
> };
550+
> takes_callback(c); // ERROR: async closure does not implement `Fn`
551+
> }
552+
> ```
553+
>
554+
> The exception of the the second clause can be illustrated by using a dereference, which does allow `Fn` and `FnMut` to be implemented:
555+
>
556+
> ```rust
557+
> fn takes_callback<Fut: Future>(c: impl Fn() -> Fut) {}
558+
>
559+
> fn f() {
560+
> let x = &1i32;
561+
> let c = async move || {
562+
> let a = *x + 2;
563+
> };
564+
> takes_callback(c); // OK: implements `Fn`
565+
> }
566+
> ```
567+
568+
r[type.closure.async.traits.async-family]
569+
Async closures implement [`AsyncFn`], [`AsyncFnMut`], and [`AsyncFnOnce`] in an analogous way as regular closures implement [`Fn`], [`FnMut`], and [`FnOnce`]; that is, depending on the use of the captured variables in its body.
570+
571+
### Other traits
508572
509573
r[type.closure.traits]
510574

src/types/function-pointer.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Function pointer types, written using the `fn` keyword, refer to a function
3131
whose identity is not necessarily known at compile-time.
3232

3333
r[type.fn-pointer.coercion]
34-
They can be created via a coercion from both [function items] and non-capturing [closures].
34+
They can be created via a coercion from both [function items] and non-capturing, non-async [closures].
3535

3636
r[type.fn-pointer.qualifiers]
3737
The `unsafe` qualifier indicates that the type's value is an [unsafe

0 commit comments

Comments
 (0)