diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a0e5f3a6f..b1ba24137 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -67,6 +67,7 @@ - [If and if let expressions](expressions/if-expr.md) - [Match expressions](expressions/match-expr.md) - [Return expressions](expressions/return-expr.md) + - [Await expressions](expressions/await-expr.md) - [Patterns](patterns.md) diff --git a/src/expressions.md b/src/expressions.md index c211fa933..75ea56478 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -13,6 +13,7 @@ >       | [_OperatorExpression_]\ >       | [_GroupedExpression_]\ >       | [_ArrayExpression_]\ +>       | [_AwaitExpression_]\ >       | [_IndexExpression_]\ >       | [_TupleExpression_]\ >       | [_TupleIndexingExpression_]\ @@ -33,6 +34,7 @@ >    [_OuterAttribute_]\*[†](#expression-attributes)\ >    (\ >          [_BlockExpression_]\ +>       | [_AsyncBlockExpression_]\ >       | [_UnsafeBlockExpression_]\ >       | [_LoopExpression_]\ >       | [_IfExpression_]\ @@ -324,6 +326,8 @@ They are never allowed before: [_ArithmeticOrLogicalExpression_]: expressions/operator-expr.md#arithmetic-and-logical-binary-operators [_ArrayExpression_]: expressions/array-expr.md +[_AsyncBlockExpression_]: expressions/block-expr.md#async-blocks +[_AwaitExpression_]: expressions/await-expr.md [_AssignmentExpression_]: expressions/operator-expr.md#assignment-expressions [_BlockExpression_]: expressions/block-expr.md [_BreakExpression_]: expressions/loop-expr.md#break-expressions diff --git a/src/expressions/await-expr.md b/src/expressions/await-expr.md new file mode 100644 index 000000000..95037d73d --- /dev/null +++ b/src/expressions/await-expr.md @@ -0,0 +1,68 @@ +# Await expressions + +> **Syntax**\ +> _AwaitExpression_ :\ +>    [_Expression_] `.` `await` + +Await expressions are legal only within an [async context], like an +[`async fn`] or an [`async` block]. They operate on a [future]. Their effect +is to suspend the current computation until the given future is ready +to produce a value. + +More specifically, an `.await` expression has the following effect. + +1. Evaluate `` to a [future] `tmp`; +2. Pin `tmp` using [`Pin::new_unchecked`]; +3. This pinned future is then polled by calling the [`Future::poll`] method and + passing it the current [task context](#task-context); +3. If the call to `poll` returns [`Poll::Pending`], then the future + returns `Poll::Pending`, suspending its state so that, when the + surrounding async context is re-polled, execution returns to step + 2; +4. Otherwise the call to `poll` must have returned [`Poll::Ready`], in which case the + value contained in the [`Poll::Ready`] variant is used as the result + of the `await` expression itself. + +[`async fn`]: ../items/functions.md#async-functions +[`async` block]: block-expr.md#async-blocks +[future]: ../../std/future/trait.Future.html +[_Expression_]: ../expressions.md +[`Future::poll`]: ../../std/future/trait.Future.html#tymethod.poll +[`Context`]: ../../std/task/struct.Context.html +[`Pin::new_unchecked`]: ../../std/pin/struct.Pin.html#method.new_unchecked +[`Poll::Pending`]: ../../std/task/enum.Poll.html#variant.Pending +[`Poll::Ready`]: ../../std/task/enum.Poll.html#variant.Ready + +> **Edition differences**: Await expressions are only available beginning with +> Rust 2018. + +## Task context + +The task context refers to the [`Context`] which was supplied to the +current [async context] when the async context itself was +polled. Because `await` expressions are only legal in an async +context, there must be some task context available. + +[`Context`]: ../../std/task/struct.Context.html +[async context]: ../expressions/block-expr.md#async-context + +## Approximate desugaring + +Effectively, an `.await` expression is roughly +equivalent to the following (this desugaring is not normative): + +```rust,ignore +let future = /* */; +loop { + let mut pin = unsafe { Pin::new_unchecked(&mut future) }; + match Pin::future::poll(Pin::borrow(&mut pin), &mut current_context) { + Poll::Ready(r) => break r, + Poll::Pending => yield Poll::Pending, + } +} +``` + +where the `yield` pseudo-code returns `Poll::Pending` and, when +re-invoked, resumes execution from that point. The variable +`current_context` refers to the context taken from the async +environment. diff --git a/src/expressions/block-expr.md b/src/expressions/block-expr.md index cfc7b0b08..9362ab9a7 100644 --- a/src/expressions/block-expr.md +++ b/src/expressions/block-expr.md @@ -80,6 +80,74 @@ fn move_by_block_expression() { } ``` +## `async` blocks + +> **Syntax**\ +> _AsyncBlockExpression_ :\ +>    `async` `move`? _BlockExpression_ + +An *async block* is a variant of a block expression which evaluates to +a *future*. The final expression of the block, if present, determines +the result value of the future. + +Executing an async block is similar to executing a closure expression: +its immediate effect is to produce and return an anonymous type. +Whereas closures return a type that implements one or more of the +[`std::ops::Fn`] traits, however, the type returned for an async block +implements the [`std::future::Future`] trait. The actual data format for +this type is unspecified. + +> **Note:** The future type that rustc generates is roughly equivalent +> to an enum with one variant per `await` point, where each variant +> stores the data needed to resume from its corresponding point. + +> **Edition differences**: Async blocks are only available beginning with Rust 2018. + +[`std::ops::Fn`]: ../../std/ops/trait.Fn.html +[`std::future::Future`]: ../../std/future/trait.Future.html + +### Capture modes + +Async blocks capture variables from their environment using the same +[capture modes] as closures. Like closures, when written `async { +.. }` the capture mode for each variable will be inferred from the +content of the block. `async move { .. }` blocks however will move all +referenced variables into the resulting future. + +[capture modes]: ../types/closure.md#capture-modes +[shared references]: ../types/pointer.md#shared-references- +[mutable reference]: ../types/pointer.md#mutables-references- + +### Async context + +Because async blocks construct a future, they define an **async +context** which can in turn contain [`await` expressions]. Async +contexts are established by async blocks as well as the bodies of +async functions, whose semantics are defined in terms of async blocks. + +[`await` expressions]: await-expr.md + +### Control-flow operators + +Async blocks act like a function boundary, much like +closures. Therefore, the `?` operator and `return` expressions both +affect the output of the future, not the enclosing function or other +context. That is, `return ` from within a closure will return +the result of `` as the output of the future. Similarly, if +`?` propagates an error, that error is propagated as the result +of the future. + +Finally, the `break` and `continue` keywords cannot be used to branch +out from an async block. Therefore the following is illegal: + +```rust,edition2018,compile_fail +loop { + async move { + break; // This would break out of the loop. + } +} +``` + ## `unsafe` blocks > **Syntax**\ diff --git a/src/items/functions.md b/src/items/functions.md index bae5b8eff..44236c273 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -8,7 +8,10 @@ >       [_BlockExpression_] > > _FunctionQualifiers_ :\ ->    `const`? `unsafe`? (`extern` _Abi_?)? +>    _AsyncConstQualifiers_? `unsafe`? (`extern` _Abi_?)? +> +> _AsyncConstQualifiers_ :\ +>    `async` | `const` > > _Abi_ :\ >    [STRING_LITERAL] | [RAW_STRING_LITERAL] @@ -189,6 +192,104 @@ Exhaustive list of permitted structures in const functions: the following unsafe operations: * calls to const unsafe functions +## Async functions + +Functions may be qualified as async, and this can also be combined with the +`unsafe` qualifier: + +```rust,edition2018 +async fn regular_example() { } +async unsafe fn unsafe_example() { } +``` + +Async functions do no work when called: instead, they +capture their arguments into a future. When polled, that future will +execute the function's body. + +An async function is roughly equivalent to a function +that returns [`impl Future`] and with an [`async move` block][async-blocks] as +its body: + +```rust,edition2018 +// Source +async fn example(x: &str) -> usize { + x.len() +} +``` + +is roughly equivalent to: + +```rust,edition2018 +# use std::future::Future; +// Desugared +fn example<'a>(x: &'a str) -> impl Future + 'a { + async move { x.len() } +} +``` + +The actual desugaring is more complex: + +- The return type in the desugaring is assumed to capture all lifetime + parameters from the `async fn` declaration. This can be seen in the + desugared example above, which explicitly outlives, and hence + captures, `'a`. +- The [`async move` block][async-blocks] in the body captures all function + parameters, including those that are unused or bound to a `_` + pattern. This ensures that function parameters are dropped in the + same order as they would be if the function were not async, except + that the drop occurs when the returned future has been fully + awaited. + +For more information on the effect of async, see [`async` blocks][async-blocks]. + +[async-blocks]: ../expressions/block-expr.md#async-blocks +[`impl Future`]: ../types/impl-trait.md + +> **Edition differences**: Async functions are only available beginning with +> Rust 2018. + +### Combining `async` and `unsafe` + +It is legal to declare a function that is both async and unsafe. The +resulting function is unsafe to call and (like any async function) +returns a future. This future is just an ordinary future and thus an +`unsafe` context is not required to "await" it: + +```rust,edition2018 +// Returns a future that, when awaited, dereferences `x`. +// +// Soundness condition: `x` must be safe to dereference until +// the resulting future is complete. +async unsafe fn unsafe_example(x: *const i32) -> i32 { + *x +} + +async fn safe_example() { + // An `unsafe` block is required to invoke the function initially: + let p = 22; + let future = unsafe { unsafe_example(&p) }; + + // But no `unsafe` block required here. This will + // read the value of `p`: + let q = future.await; +} +``` + +Note that this behavior is a consequence of the desugaring to a +function that returns an `impl Future` -- in this case, the function +we desugar to is an `unsafe` function, but the return value remains +the same. + +Unsafe is used on an async function in precisely the same way that it +is used on other functions: it indicates that the function imposes +some additional obligations on its caller to ensure soundness. As in any +other unsafe function, these conditions may extend beyond the initial +call itself -- in the snippet above, for example, the `unsafe_example` +function took a pointer `x` as argument, and then (when awaited) +dereferenced that pointer. This implies that `x` would have to be +valid until the future is finished executing, and it is the callers +responsibility to ensure that. + ## Attributes on functions [Outer attributes][attributes] are allowed on functions. [Inner