diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0f9a1466d8fc..341ac6016131 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -232,12 +232,7 @@ - [Async](async.md) - [async/await](async/async-await.md) - [Async Blocks](async/async-blocks.md) - - Futures - - Executors - - Polling - - Pin - - Channels - - Select + - [Futures](async/futures.md) - [Exercises](exercises/day-4/async.md) # Final Words diff --git a/src/async/async-await.md b/src/async/async-await.md index 4414c9b367cd..b0a2e4278ba7 100644 --- a/src/async/async-await.md +++ b/src/async/async-await.md @@ -2,11 +2,11 @@ At a high level, async Rust code looks very much like "normal" sequential code: -```rust,editable +```rust,editable,compile_fail use tokio::time; -async fn count_to(i: i32) { - for i in 1..10 { +async fn count_to(count: i32) { + for i in 1..=count { println!("Count in task: {i}!"); time::sleep(time::Duration::from_millis(5)).await; } diff --git a/src/async/async-blocks.md b/src/async/async-blocks.md index 57115c1fc88d..8ec21a63df8e 100644 --- a/src/async/async-blocks.md +++ b/src/async/async-blocks.md @@ -3,7 +3,7 @@ Similar to closures, a snippet of async code can be included inline in another function with an async block: -```rust, editable +```rust,editable,compile_fail use tokio::{time, task}; #[tokio::main] diff --git a/src/async/futures.md b/src/async/futures.md new file mode 100644 index 000000000000..0e2ca784ce8b --- /dev/null +++ b/src/async/futures.md @@ -0,0 +1,72 @@ +# Futures + +What is the type of an async operation? + +```rust,editable,compile_fail +use tokio::time; + +async fn count_to(count: i32) -> i32 { + for i in 1..=count { + println!("Count in task: {i}!"); + time::sleep(time::Duration::from_millis(5)).await; + } + count +} + +#[tokio::main] +async fn main() { + let _: () = count_to(13); +} +``` + +[Future](https://doc.rust-lang.org/nightly/src/core/future/future.rs.html#37) +is a trait, implemented by objects that represent an operation that may not be +complete yet. A future can be polled, and `poll` returns either +`Poll::Ready(result)` or `Poll::Pending`. + +```rust +use std::pin::Pin; +use std::task::Context; + +pub trait Future { + type Output; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; +} + +pub enum Poll { + Ready(T), + Pending, +} +``` + +An async function returns an `impl Future`, and an async block evaluates to an +`impl Future`. It's also possible (but uncommon) to implement `Future` for your +own types. For example, the `JoinHandle` returned from `tokio::spawn` implements +`Future` to allow joining to it. + +The `.await` keyword, applied to a Future, causes the current async function or +block to pause until that Future is ready, and then evaluates to its output. + +An important difference from other languages is that a Future is inert: it does +not do anything until it is polled. + +
+ +* Run the example and look at the error message. `_: () = ..` is a common + technique for getting the type of an expression. Try adding a `.await` in + `main`. + +* The `Future` and `Poll` types are conceptually quite simple, and implemented as + such in `std::task`. + +* We will not get to `Pin` and `Context`, as we will focus on writing async + code, rather than building new async primitives. Briefly: + + * `Context` allows a Future to schedule itself to be polled again when an + event occurs. + + * `Pin` ensures that the Future isn't moved in memory, so that pointers into + that future remain valid. This is required to allow references to remain + valid after an `.await`. + +