Skip to content

Commit

Permalink
Ch. 17: corrections/discussion on runtimes, mostly
Browse files Browse the repository at this point in the history
- Tie off the discussion about needing a runtime so that it is clear
  what is needed in `main` and therefore why `main` cannot natively be
  `async` itself.
- Correct the description of what `.await` compiles to.
- Extend the note about the “under the hood” bits: mention generators so
  people know what to go looking for if they are curious.
- Rewrite the existing introduction of the `#[async_main]` macro to lean
  on the material now covered in the previous chapter.
  • Loading branch information
chriskrycho committed May 10, 2024
1 parent 2015179 commit d61b736
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 30 deletions.
49 changes: 28 additions & 21 deletions src/ch17-01-tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,38 +264,45 @@ loop {
```

And in fact, when we use `.await`, Rust compiles it to something very similar to
that `loop`. However, if Rust compiled it to *only* that code, every `.await`
would block the computer from doing anything else—the opposite of what we were
going for!

> Note: It also wouldn’t compile, because it doesn’t actually satisfy the
> contract for a `Future`. In particular, `hello_async_fut` is not *pinned* with
> the `Pin` type and we did not pass along a `Context` argument. The details are
> beyond the scope of this book, but are well worth digging into. In particular,
> see [Chapter 4: Pinning][pinning] in the official [_Asynchronous Programming
> in Rust_][async-book] book.
<!-- TODO: listing number -->

What is more, eventually we end up back in some non-async function. At that
point, something needs to “translate” between the async and sync worlds. That
“something” is a *runtime*, a crate which handles the top-level `poll()` call,
scheduling and handing off between the different async operations which may be
in flight, and often providing async versions of functionality like file I/O.
that loop. If Rust compiled it to *exactly* that code, every `.await` would
block the computer from doing anything else—the opposite of what we were going
for! Instead, Rust internally makes sure that the loop can hand back control to
the the context of the code where which is awaiting this little bit of code.

When we follow that chain far enough, eventually we end up back in some
non-async function. At that point, something needs to “translate” between the
async and sync worlds. That “something” is a *runtime*, a crate which handles
the top-level `poll()` call, scheduling and handing off between the different
async operations which may be in flight, and often providing async versions of
functionality like file I/O.

Now we can understand what is happening in Listing 17-XX. The `main` function is
not `async`—and it really cannot be: if it were, something would need to call
`poll()` on whatever `main` returned! Instead, we use the `trpl::block_on`
function, which polls the `Future` returned by `hello_async` until it returns
`Ready`.

### Running Async Code
`Ready`. Every async program in Rust has at least one place where it sets up an
executor and executes code.

> Note: Under the hood, Rust uses *generators* so that it can hand off control
> between different functions. These are an implementation detail, though, and
> you never have to think about it when writing Rust.
>
> The loop as written also wouldn’t compile, because it doesn’t actually satisfy
> the contract for a `Future`. In particular, `hello_async_fut` is not *pinned*
> with the `Pin` type and we did not pass along a `Context` argument.
>
> More details here are beyond the scope of this book, but are well worth
> digging into if you want to understand how things work “under the hood.” In
> particular, see [Chapter 2: Under the Hood: Executing Futures and
> Tasks][under-the-hood] and [Chapter 4: Pinning][pinning] in the official
> [_Asynchronous Programming in Rust_][async-book] book.
Now, that’s a lot of work to just print a string, but we have laid some key
foundations for working with async in Rust! Now that you know the basics of how
futures and runtimes work, we can see some of the things we can *do* with async.

[impl-trait]: ch10-02-traits.html#traits-as-parameters
[under-the-hood]: https://rust-lang.github.io/async-book/02_execution/01_chapter.html
[pinning]: https://rust-lang.github.io/async-book/04_pinning/01_chapter.html
[async-book]: https://rust-lang.github.io/async-book/
[crate-source]: https://github.com/rust-lang/book/tree/main/packages/trpl
Expand Down
19 changes: 10 additions & 9 deletions src/ch17-02.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
The executor from `futures` we used in the previous section is intentionally
quite limited. It works quite well, but if you are going to build a real-world
application, you will likely want to use the executor from one of the *other*
runtimes in the Rust ecosystem.
quite limited. It works well, but it requires you to set it up yourself, as we
did in the previous chapter, and it it is not tuned for any particular use case.
Accordingly, if you are going to build a real-world application, you will likely
want to use the executor from one of the *other* runtimes in the Rust
ecosystem. Once again, we will use code from the `trpl` crate to set things up.

> ### The `futures` and `tokio` Crates
>
Expand All @@ -18,12 +20,10 @@ runtimes in the Rust ecosystem.
> because it is the most widely-used runtime—not as a judgment call on whether
> it is the *best* runtime!
Okay, now let’s start exploring. Going forward, we will use a new `async_main`
macro so that we can use `async fn` with a `main` function. Under the hood, this
little utility macro from Tokio sets up the Tokio runtime and wires up a call
kind of like we saw with `futures::executor::block_on` in the previous section.
Now we can use `async` blocks directly in `main` and not worry about wiring up
the runtime ourselves:
This time, it uses a macro, `async_main`, which allows us to use `async fn` with
a `main` function. The macro just rewrites the function to do something similar
to what we wrote by hand in the previous chapter, but lets us skip writing it
out by hand. Now we can write that `async` block and `.await` it in `main`:

```rust
use trpl::async_main;
Expand All @@ -36,6 +36,7 @@ async fn main() {
}
```

Okay, now let’s start exploring.


[tokio]: https://tokio.rs
Expand Down

0 comments on commit d61b736

Please sign in to comment.