Skip to content

Commit b1844a2

Browse files
authored
Merge pull request #635 from nikomatsakis/async-await
async-await initial reference material
2 parents 2b29a56 + e0b5db6 commit b1844a2

File tree

5 files changed

+243
-1
lines changed

5 files changed

+243
-1
lines changed

src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
- [If and if let expressions](expressions/if-expr.md)
6868
- [Match expressions](expressions/match-expr.md)
6969
- [Return expressions](expressions/return-expr.md)
70+
- [Await expressions](expressions/await-expr.md)
7071

7172
- [Patterns](patterns.md)
7273

src/expressions.md

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
>       | [_OperatorExpression_]\
1414
>       | [_GroupedExpression_]\
1515
>       | [_ArrayExpression_]\
16+
>       | [_AwaitExpression_]\
1617
>       | [_IndexExpression_]\
1718
>       | [_TupleExpression_]\
1819
>       | [_TupleIndexingExpression_]\
@@ -33,6 +34,7 @@
3334
> &nbsp;&nbsp; [_OuterAttribute_]<sup>\*</sup>[](#expression-attributes)\
3435
> &nbsp;&nbsp; (\
3536
> &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; [_BlockExpression_]\
37+
> &nbsp;&nbsp; &nbsp;&nbsp; | [_AsyncBlockExpression_]\
3638
> &nbsp;&nbsp; &nbsp;&nbsp; | [_UnsafeBlockExpression_]\
3739
> &nbsp;&nbsp; &nbsp;&nbsp; | [_LoopExpression_]\
3840
> &nbsp;&nbsp; &nbsp;&nbsp; | [_IfExpression_]\
@@ -324,6 +326,8 @@ They are never allowed before:
324326

325327
[_ArithmeticOrLogicalExpression_]: expressions/operator-expr.md#arithmetic-and-logical-binary-operators
326328
[_ArrayExpression_]: expressions/array-expr.md
329+
[_AsyncBlockExpression_]: expressions/block-expr.md#async-blocks
330+
[_AwaitExpression_]: expressions/await-expr.md
327331
[_AssignmentExpression_]: expressions/operator-expr.md#assignment-expressions
328332
[_BlockExpression_]: expressions/block-expr.md
329333
[_BreakExpression_]: expressions/loop-expr.md#break-expressions

src/expressions/await-expr.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Await expressions
2+
3+
> **<sup>Syntax</sup>**\
4+
> _AwaitExpression_ :\
5+
> &nbsp;&nbsp; [_Expression_] `.` `await`
6+
7+
Await expressions are legal only within an [async context], like an
8+
[`async fn`] or an [`async` block]. They operate on a [future]. Their effect
9+
is to suspend the current computation until the given future is ready
10+
to produce a value.
11+
12+
More specifically, an `<expr>.await` expression has the following effect.
13+
14+
1. Evaluate `<expr>` to a [future] `tmp`;
15+
2. Pin `tmp` using [`Pin::new_unchecked`];
16+
3. This pinned future is then polled by calling the [`Future::poll`] method and
17+
passing it the current [task context](#task-context);
18+
3. If the call to `poll` returns [`Poll::Pending`], then the future
19+
returns `Poll::Pending`, suspending its state so that, when the
20+
surrounding async context is re-polled, execution returns to step
21+
2;
22+
4. Otherwise the call to `poll` must have returned [`Poll::Ready`], in which case the
23+
value contained in the [`Poll::Ready`] variant is used as the result
24+
of the `await` expression itself.
25+
26+
[`async fn`]: ../items/functions.md#async-functions
27+
[`async` block]: block-expr.md#async-blocks
28+
[future]: ../../std/future/trait.Future.html
29+
[_Expression_]: ../expressions.md
30+
[`Future::poll`]: ../../std/future/trait.Future.html#tymethod.poll
31+
[`Context`]: ../../std/task/struct.Context.html
32+
[`Pin::new_unchecked`]: ../../std/pin/struct.Pin.html#method.new_unchecked
33+
[`Poll::Pending`]: ../../std/task/enum.Poll.html#variant.Pending
34+
[`Poll::Ready`]: ../../std/task/enum.Poll.html#variant.Ready
35+
36+
> **Edition differences**: Await expressions are only available beginning with
37+
> Rust 2018.
38+
39+
## Task context
40+
41+
The task context refers to the [`Context`] which was supplied to the
42+
current [async context] when the async context itself was
43+
polled. Because `await` expressions are only legal in an async
44+
context, there must be some task context available.
45+
46+
[`Context`]: ../../std/task/struct.Context.html
47+
[async context]: ../expressions/block-expr.md#async-context
48+
49+
## Approximate desugaring
50+
51+
Effectively, an `<expr>.await` expression is roughly
52+
equivalent to the following (this desugaring is not normative):
53+
54+
```rust,ignore
55+
let future = /* <expr> */;
56+
loop {
57+
let mut pin = unsafe { Pin::new_unchecked(&mut future) };
58+
match Pin::future::poll(Pin::borrow(&mut pin), &mut current_context) {
59+
Poll::Ready(r) => break r,
60+
Poll::Pending => yield Poll::Pending,
61+
}
62+
}
63+
```
64+
65+
where the `yield` pseudo-code returns `Poll::Pending` and, when
66+
re-invoked, resumes execution from that point. The variable
67+
`current_context` refers to the context taken from the async
68+
environment.

src/expressions/block-expr.md

+68
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,74 @@ fn move_by_block_expression() {
8080
}
8181
```
8282

83+
## `async` blocks
84+
85+
> **<sup>Syntax</sup>**\
86+
> _AsyncBlockExpression_ :\
87+
> &nbsp;&nbsp; `async` `move`<sup>?</sup> _BlockExpression_
88+
89+
An *async block* is a variant of a block expression which evaluates to
90+
a *future*. The final expression of the block, if present, determines
91+
the result value of the future.
92+
93+
Executing an async block is similar to executing a closure expression:
94+
its immediate effect is to produce and return an anonymous type.
95+
Whereas closures return a type that implements one or more of the
96+
[`std::ops::Fn`] traits, however, the type returned for an async block
97+
implements the [`std::future::Future`] trait. The actual data format for
98+
this type is unspecified.
99+
100+
> **Note:** The future type that rustc generates is roughly equivalent
101+
> to an enum with one variant per `await` point, where each variant
102+
> stores the data needed to resume from its corresponding point.
103+
104+
> **Edition differences**: Async blocks are only available beginning with Rust 2018.
105+
106+
[`std::ops::Fn`]: ../../std/ops/trait.Fn.html
107+
[`std::future::Future`]: ../../std/future/trait.Future.html
108+
109+
### Capture modes
110+
111+
Async blocks capture variables from their environment using the same
112+
[capture modes] as closures. Like closures, when written `async {
113+
.. }` the capture mode for each variable will be inferred from the
114+
content of the block. `async move { .. }` blocks however will move all
115+
referenced variables into the resulting future.
116+
117+
[capture modes]: ../types/closure.md#capture-modes
118+
[shared references]: ../types/pointer.md#shared-references-
119+
[mutable reference]: ../types/pointer.md#mutables-references-
120+
121+
### Async context
122+
123+
Because async blocks construct a future, they define an **async
124+
context** which can in turn contain [`await` expressions]. Async
125+
contexts are established by async blocks as well as the bodies of
126+
async functions, whose semantics are defined in terms of async blocks.
127+
128+
[`await` expressions]: await-expr.md
129+
130+
### Control-flow operators
131+
132+
Async blocks act like a function boundary, much like
133+
closures. Therefore, the `?` operator and `return` expressions both
134+
affect the output of the future, not the enclosing function or other
135+
context. That is, `return <expr>` from within a closure will return
136+
the result of `<expr>` as the output of the future. Similarly, if
137+
`<expr>?` propagates an error, that error is propagated as the result
138+
of the future.
139+
140+
Finally, the `break` and `continue` keywords cannot be used to branch
141+
out from an async block. Therefore the following is illegal:
142+
143+
```rust,edition2018,compile_fail
144+
loop {
145+
async move {
146+
break; // This would break out of the loop.
147+
}
148+
}
149+
```
150+
83151
## `unsafe` blocks
84152

85153
> **<sup>Syntax</sup>**\

src/items/functions.md

+102-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
> &nbsp;&nbsp; &nbsp;&nbsp; [_BlockExpression_]
99
>
1010
> _FunctionQualifiers_ :\
11-
> &nbsp;&nbsp; `const`<sup>?</sup> `unsafe`<sup>?</sup> (`extern` _Abi_<sup>?</sup>)<sup>?</sup>
11+
> &nbsp;&nbsp; _AsyncConstQualifiers_<sup>?</sup> `unsafe`<sup>?</sup> (`extern` _Abi_<sup>?</sup>)<sup>?</sup>
12+
>
13+
> _AsyncConstQualifiers_ :\
14+
> &nbsp;&nbsp; `async` | `const`
1215
>
1316
> _Abi_ :\
1417
> &nbsp;&nbsp; [STRING_LITERAL] | [RAW_STRING_LITERAL]
@@ -227,6 +230,104 @@ Exhaustive list of permitted structures in const functions:
227230
the following unsafe operations:
228231
* calls to const unsafe functions
229232

233+
## Async functions
234+
235+
Functions may be qualified as async, and this can also be combined with the
236+
`unsafe` qualifier:
237+
238+
```rust,edition2018
239+
async fn regular_example() { }
240+
async unsafe fn unsafe_example() { }
241+
```
242+
243+
Async functions do no work when called: instead, they
244+
capture their arguments into a future. When polled, that future will
245+
execute the function's body.
246+
247+
An async function is roughly equivalent to a function
248+
that returns [`impl Future`] and with an [`async move` block][async-blocks] as
249+
its body:
250+
251+
```rust,edition2018
252+
// Source
253+
async fn example(x: &str) -> usize {
254+
x.len()
255+
}
256+
```
257+
258+
is roughly equivalent to:
259+
260+
```rust,edition2018
261+
# use std::future::Future;
262+
// Desugared
263+
fn example<'a>(x: &'a str) -> impl Future<Output = usize> + 'a {
264+
async move { x.len() }
265+
}
266+
```
267+
268+
The actual desugaring is more complex:
269+
270+
- The return type in the desugaring is assumed to capture all lifetime
271+
parameters from the `async fn` declaration. This can be seen in the
272+
desugared example above, which explicitly outlives, and hence
273+
captures, `'a`.
274+
- The [`async move` block][async-blocks] in the body captures all function
275+
parameters, including those that are unused or bound to a `_`
276+
pattern. This ensures that function parameters are dropped in the
277+
same order as they would be if the function were not async, except
278+
that the drop occurs when the returned future has been fully
279+
awaited.
280+
281+
For more information on the effect of async, see [`async` blocks][async-blocks].
282+
283+
[async-blocks]: ../expressions/block-expr.md#async-blocks
284+
[`impl Future`]: ../types/impl-trait.md
285+
286+
> **Edition differences**: Async functions are only available beginning with
287+
> Rust 2018.
288+
289+
### Combining `async` and `unsafe`
290+
291+
It is legal to declare a function that is both async and unsafe. The
292+
resulting function is unsafe to call and (like any async function)
293+
returns a future. This future is just an ordinary future and thus an
294+
`unsafe` context is not required to "await" it:
295+
296+
```rust,edition2018
297+
// Returns a future that, when awaited, dereferences `x`.
298+
//
299+
// Soundness condition: `x` must be safe to dereference until
300+
// the resulting future is complete.
301+
async unsafe fn unsafe_example(x: *const i32) -> i32 {
302+
*x
303+
}
304+
305+
async fn safe_example() {
306+
// An `unsafe` block is required to invoke the function initially:
307+
let p = 22;
308+
let future = unsafe { unsafe_example(&p) };
309+
310+
// But no `unsafe` block required here. This will
311+
// read the value of `p`:
312+
let q = future.await;
313+
}
314+
```
315+
316+
Note that this behavior is a consequence of the desugaring to a
317+
function that returns an `impl Future` -- in this case, the function
318+
we desugar to is an `unsafe` function, but the return value remains
319+
the same.
320+
321+
Unsafe is used on an async function in precisely the same way that it
322+
is used on other functions: it indicates that the function imposes
323+
some additional obligations on its caller to ensure soundness. As in any
324+
other unsafe function, these conditions may extend beyond the initial
325+
call itself -- in the snippet above, for example, the `unsafe_example`
326+
function took a pointer `x` as argument, and then (when awaited)
327+
dereferenced that pointer. This implies that `x` would have to be
328+
valid until the future is finished executing, and it is the callers
329+
responsibility to ensure that.
330+
230331
## Attributes on functions
231332

232333
[Outer attributes][attributes] are allowed on functions. [Inner

0 commit comments

Comments
 (0)