Skip to content

Commit

Permalink
Add futures page (#497)
Browse files Browse the repository at this point in the history
NOTE: `mdbook test` does not allow code samples to reference other crates, so
they must be marked as `compile_fail`; see #175.
  • Loading branch information
djmitche authored Mar 22, 2023
1 parent 7515eb2 commit 665e898
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
7 changes: 1 addition & 6 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/async/async-await.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/async/async-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
72 changes: 72 additions & 0 deletions src/async/futures.md
Original file line number Diff line number Diff line change
@@ -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<Self::Output>;
}

pub enum Poll<T> {
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.

<details>

* 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`.

</details>

0 comments on commit 665e898

Please sign in to comment.