diff --git a/mdbook-spec/src/std_links.rs b/mdbook-spec/src/std_links.rs index 28ca6ba51..e5b9448d1 100644 --- a/mdbook-spec/src/std_links.rs +++ b/mdbook-spec/src/std_links.rs @@ -247,7 +247,7 @@ fn run_rustdoc(tmp: &TempDir, chapter_links: &HashMap<&PathBuf, Vec>>) fs::write(&src_path, &src).unwrap(); let rustdoc = std::env::var("RUSTDOC").unwrap_or_else(|_| "rustdoc".into()); let output = Command::new(rustdoc) - .arg("--edition=2021") + .arg("--edition=2024") .arg(&src_path) .current_dir(tmp.path()) .output() diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md index ae129890f..2d233f348 100644 --- a/src/expressions/await-expr.md +++ b/src/expressions/await-expr.md @@ -8,7 +8,7 @@ An `await` expression is a syntactic construct for suspending a computation provided by an implementation of `std::future::IntoFuture` until the given future is ready to produce a value. 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. -Await expressions are legal only within an [async context], like an [`async fn`] or an [`async` block]. +Await expressions are legal only within an [async context], like an [`async fn`], [`async` closure], or [`async` block]. More specifically, an await expression has the following effect. @@ -48,6 +48,7 @@ The variable `current_context` refers to the context taken from the async enviro [_Expression_]: ../expressions.md [`async fn`]: ../items/functions.md#async-functions +[`async` closure]: closure-expr.md#async-closures [`async` block]: block-expr.md#async-blocks [`Context`]: std::task::Context [`future::poll`]: std::future::Future::poll diff --git a/src/expressions/call-expr.md b/src/expressions/call-expr.md index 64df58ce4..fc196b7a6 100644 --- a/src/expressions/call-expr.md +++ b/src/expressions/call-expr.md @@ -10,7 +10,13 @@ A *call expression* calls a function. 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*. If the function eventually returns, then the expression completes. -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. + +For [non-function types], the expression `f(...)` uses the method on one of the following traits based on the function operand: + +- [`Fn`] or [`AsyncFn`] --- shared reference. +- [`FnMut`] or [`AsyncFnMut`] --- mutable reference. +- [`FnOnce`] or [`AsyncFnOnce`] --- value. + An automatic borrow will be taken if needed. The function operand will also be [automatically dereferenced] as required. diff --git a/src/expressions/closure-expr.md b/src/expressions/closure-expr.md index 103f74795..abb3c8e9a 100644 --- a/src/expressions/closure-expr.md +++ b/src/expressions/closure-expr.md @@ -2,6 +2,7 @@ > **Syntax**\ > _ClosureExpression_ :\ +>    `async`[^cl-async-edition]?\ >    `move`?\ >    ( `||` | `|` _ClosureParameters_? `|` )\ >    ([_Expression_] | `->` [_TypeNoBounds_] [_BlockExpression_]) @@ -11,9 +12,11 @@ > > _ClosureParam_ :\ >    [_OuterAttribute_]\* [_PatternNoTopAlt_] ( `:` [_Type_] )? +> +> [^cl-async-edition]: The `async` qualifier is not allowed in the 2015 edition. A *closure expression*, also known as a lambda expression or a lambda, defines a [closure type] and evaluates to a value of that type. -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*. +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*. The optional type after each pattern is a type annotation for the pattern. If there is a return type, the closure body must be a [block]. @@ -29,10 +32,32 @@ This is often used to ensure that the closure's lifetime is `'static`. ## Closure trait implementations -Which traits the closure type implement depends on how variables are captured and the types of the captured variables. +Which traits the closure type implement depends on how variables are captured, the types of the captured variables, and the presence of `async`. See the [call traits and coercions] chapter for how and when a closure implements `Fn`, `FnMut`, and `FnOnce`. The closure type implements [`Send`] and [`Sync`] if the type of every captured variable also implements the trait. +## Async closures + +Closures marked with the `async` keyword indicate that they are asynchronous in an analogous way to an [async function][items.fn.async]. + +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. + +```rust +async fn takes_async_callback(f: impl AsyncFn(u64)) { + f(0).await; + f(1).await; +} + +async fn example() { + takes_async_callback(async |i| { + core::future::ready(i).await; + println!("done with {i}."); + }).await; +} +``` + +> **Edition differences**: Async closures are only available beginning with Rust 2018. + ## Example 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. diff --git a/src/items/traits.md b/src/items/traits.md index dd315e440..0d0a9ecbd 100644 --- a/src/items/traits.md +++ b/src/items/traits.md @@ -120,6 +120,9 @@ r[items.traits.dyn-compatible.associated-functions] * Explicitly non-dispatchable functions require: * Have a `where Self: Sized` bound (receiver type of `Self` (i.e. `self`) implies this). +r[items.traits.dyn-compatible.async-traits] +* The [`AsyncFn`], [`AsyncFnMut`], and [`AsyncFnOnce`] traits are not dyn-compatible. + > **Note**: This concept was formerly known as *object safety*. ```rust diff --git a/src/types/closure.md b/src/types/closure.md index d46d7f7ae..2fc59276d 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -94,6 +94,11 @@ let c = || { }; ``` +### Async input capture + +r[type.closure.async.input] +Async closures always capture all input arguments, regardless of whether or not they are used within the body. + ## Capture Precision r[type.closure.capture.precision.capture-path] @@ -491,7 +496,7 @@ r[type.closure.call.fn] r[type.closure.non-capturing] *Non-capturing closures* are closures that don't capture anything from their -environment. They can be coerced to function pointers (e.g., `fn()`) +environment. Non-async, non-capturing closures can be coerced to function pointers (e.g., `fn()`) with the matching signature. ```rust @@ -504,7 +509,66 @@ let bo: Binop = add; x = bo(5,7); ``` -## Other traits +### Async closure traits + +r[type.closure.async.traits] + +r[type.closure.async.traits.fn-family] +Async closures have a further restriction of whether or not they implement [`FnMut`] or [`Fn`]. + +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: + +- The `Future` includes a mutable capture. +- The async closure captures by value, except when the value is accessed with a dereference projection. + +If the async closure is lending to its `Future`, then [`FnMut`] and [`Fn`] are *not* implemented. [`FnOnce`] is always implemented. + +> **Example**: The first clause for a mutable capture can be illustrated with the following: +> +> ```rust,compile_fail +> fn takes_callback(c: impl FnMut() -> Fut) {} +> +> fn f() { +> let mut x = 1i32; +> let c = async || { +> x = 2; // x captured with MutBorrow +> }; +> takes_callback(c); // ERROR: async closure does not implement `FnMut` +> } +> ``` +> +> The second clause for a regular value capture can be illustrated with the following: +> +> ```rust,compile_fail +> fn takes_callback(c: impl Fn() -> Fut) {} +> +> fn f() { +> let x = &1i32; +> let c = async move || { +> let a = x + 2; // x captured ByValue +> }; +> takes_callback(c); // ERROR: async closure does not implement `Fn` +> } +> ``` +> +> The exception of the the second clause can be illustrated by using a dereference, which does allow `Fn` and `FnMut` to be implemented: +> +> ```rust +> fn takes_callback(c: impl Fn() -> Fut) {} +> +> fn f() { +> let x = &1i32; +> let c = async move || { +> let a = *x + 2; +> }; +> takes_callback(c); // OK: implements `Fn` +> } +> ``` + +r[type.closure.async.traits.async-family] +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. + +### Other traits r[type.closure.traits] diff --git a/src/types/function-pointer.md b/src/types/function-pointer.md index d7950b159..21a398265 100644 --- a/src/types/function-pointer.md +++ b/src/types/function-pointer.md @@ -31,7 +31,7 @@ Function pointer types, written using the `fn` keyword, refer to a function whose identity is not necessarily known at compile-time. r[type.fn-pointer.coercion] -They can be created via a coercion from both [function items] and non-capturing [closures]. +They can be created via a coercion from both [function items] and non-capturing, non-async [closures]. r[type.fn-pointer.qualifiers] The `unsafe` qualifier indicates that the type's value is an [unsafe