mirror of https://github.com/rust-lang/async-book
155 lines
5.1 KiB
Markdown
155 lines
5.1 KiB
Markdown
|
# `async`/`await!`
|
||
|
|
||
|
In [the first chapter], we took a brief look at `async`/`await!` and used
|
||
|
it to build a simple server. This chapter will discuss `async`/`await!` in
|
||
|
greater detail, explaining how it works and how `async` code differs from
|
||
|
traditional Rust programs.
|
||
|
|
||
|
`async`/`await!` are special pieces of Rust syntax that make it possible to
|
||
|
yield control of the current thread rather than blocking, allowing other
|
||
|
code to make progress while waiting on an operation to complete.
|
||
|
|
||
|
There are three main ways to use `async`: `async fn`, `async` blocks, and
|
||
|
`async` closures. Each returns a value that implements the `Future` trait:
|
||
|
|
||
|
```rust
|
||
|
// `foo()` returns a type that implements `Future<Output = u8>`.
|
||
|
// `await!(foo())` will result in a value of type `u8`.
|
||
|
async fn foo() -> u8 { 5 }
|
||
|
|
||
|
fn bar() -> impl Future<Output = u8> {
|
||
|
// This `async` block results in a type that implements
|
||
|
// `Future<Output = u8>`.
|
||
|
async {
|
||
|
let x: u8 = await!(foo());
|
||
|
x + 5
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn baz() -> impl Future<Output = u8> {
|
||
|
// This `async` closure, when called, returns a type that
|
||
|
// implements `Future<Output = u8>`
|
||
|
let closure = async |x: u8| {
|
||
|
await!(bar()) + x
|
||
|
};
|
||
|
closure(5)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
As we saw in the first chapter, `async` bodies and other futures are lazy:
|
||
|
they do nothing until they are run. The most common way to run a `Future`
|
||
|
is to `await!` it. When `await!` is called on a `Future`, it will attempt
|
||
|
to run it to completion. If the `Future` is blocked, it will yield control
|
||
|
of the current thread. When more progress can be made, the `Future` will be picked
|
||
|
up by the executor and will resume running, allowing the `await!` to resolve.
|
||
|
|
||
|
## `async` Lifetimes
|
||
|
|
||
|
Unlike traditional functions, `async fn`s which take references or other
|
||
|
non-`'static` arguments return a `Future` which is bounded by the lifetime of
|
||
|
the arguments:
|
||
|
|
||
|
```rust
|
||
|
// This function:
|
||
|
async fn foo(x: &u8) -> u8 { *x }
|
||
|
|
||
|
// Is equivalent ot this function:
|
||
|
fn foo<'a>(x: &'a u8) -> impl Future<Output = ()> + 'a {
|
||
|
async { *x }
|
||
|
}
|
||
|
```
|
||
|
|
||
|
This means that the future returned from an `async fn` must be `await!`ed
|
||
|
while its non-`'static` arguments are still valid. In the common
|
||
|
case of `await!`ing the future immediately after calling the function
|
||
|
(like `await!(foo(&x))`) this is not an issue. However, if storing the future
|
||
|
or sending it over to another task or thread, this may be an issue.
|
||
|
|
||
|
One common workaround for turning an `async fn` with references-as-arguments
|
||
|
into a `'static` future is to bundle the arguments with the call to the
|
||
|
`async fn` inside an `async` block:
|
||
|
|
||
|
```rust
|
||
|
async fn foo(x: &u8) -> u8 { *x }
|
||
|
|
||
|
fn bad() -> impl Future<Output = ()> {
|
||
|
let x = 5;
|
||
|
foo(&x) // ERROR: `x` does not live long enough
|
||
|
}
|
||
|
|
||
|
fn good() -> impl Future<Output = ()> {
|
||
|
async {
|
||
|
let x = 5;
|
||
|
await!(foo(&x))
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
By moving the argument into the `async` block, we extend its lifetime to match
|
||
|
that of the `Future` returned from the call to `foo`.
|
||
|
|
||
|
## `async move`
|
||
|
|
||
|
`async` blocks and closures allow the `move` keyword, much like normal
|
||
|
closures. An `async move` block will take ownership of the variables it
|
||
|
references, allowing it to outlive the current scope, but giving up the ability
|
||
|
to share those variables with other code:
|
||
|
|
||
|
```rust
|
||
|
/// `async` block:
|
||
|
///
|
||
|
/// Multiple different `async` blocks can access the same local variable
|
||
|
/// so long as they're executed within the variable's scope.
|
||
|
async fn foo() {
|
||
|
let my_string = "foo".to_string();
|
||
|
|
||
|
let future_one = async {
|
||
|
...
|
||
|
println!("{}", my_string);
|
||
|
};
|
||
|
|
||
|
let future_two = async {
|
||
|
...
|
||
|
println!("{}", my_string);
|
||
|
};
|
||
|
|
||
|
// Run both futures to completion, printing "foo" twice
|
||
|
let ((), ()) = join!(future_one, future_two);
|
||
|
}
|
||
|
|
||
|
/// `async move` block:
|
||
|
///
|
||
|
/// Only one `async` block can access captured variables, since they are
|
||
|
/// moved into the `Future` generated by the `async` block. However,
|
||
|
/// this allows the `Future` to outlive the original scope of the variable:
|
||
|
fn foo() -> impl Future<Output = ()> {
|
||
|
let my_string = "foo".to_string();
|
||
|
async move {
|
||
|
...
|
||
|
println!("{}", my_string);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## `await!`ing on a Multithreaded Executor
|
||
|
|
||
|
Note that, when using a multithreaded `Future` executor, a `Future` may move
|
||
|
between threads, so any variables used in `async` bodies must be able to travel
|
||
|
between threads, as any `await!` can potentially result in a switch to a new
|
||
|
thread.
|
||
|
|
||
|
This means that it is not safe to use `Rc`, `&RefCell` or any other types
|
||
|
that don't implement the `Send` trait, including references to types that don't
|
||
|
implement the `Sync` trait.
|
||
|
|
||
|
(Caveat: it is possible to use these types so long as they aren't in scope
|
||
|
during a call to `await!`.)
|
||
|
|
||
|
Similarly, it isn't a good idea to hold a traditional non-futures-aware lock
|
||
|
across an `await!`, as it can cause the threadpool to lock up: one task could
|
||
|
take out a lock, `await!` and yield to the executor, allowing another task to
|
||
|
attempt to take the lock and cause a deadlock. To avoid this, use the `Mutex`
|
||
|
in `futures::lock` rather than the one from `std::sync`.
|
||
|
|
||
|
[the first chapter]: TODO ../getting_started/async_await_primer.md
|