refactored intro and first chapter
This commit is contained in:
@@ -5,82 +5,39 @@ This book aims to explain `Futures` in Rust using an example driven approach.
|
||||
The goal is to get a better understanding of `Futures` by implementing a toy
|
||||
`Reactor`, a very simple `Executor` and our own `Futures`.
|
||||
|
||||
We'll start off a bit untraditionally. Instead of deferring some of the details about what's
|
||||
special about futures in Rust we try to tackle that head on first. We'll be as brief as possible,
|
||||
but as thorough as needed. I findt that implementing and understanding `Futures` is a lot easier
|
||||
then. Actually, most questions will be answered up front.
|
||||
We'll start off a bit differently than most other explanations. Instead of
|
||||
deferring some of the details about what's special about futures in Rust we
|
||||
try to tackle that head on first. We'll be as brief as possible, but as thorough
|
||||
as needed. This way, most question will be answered and explored up front.
|
||||
|
||||
We'll end up with futures that can run an any executor like `tokio` and `async_str`.
|
||||
|
||||
In the end I've made some reader excercises you can do if you want to fix some
|
||||
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||
In the end I've made some reader exercises you can do if you want to fix some
|
||||
of the most glaring omissions and shortcuts we took and create a slightly better
|
||||
example yourself.
|
||||
|
||||
## What does this book give you that isn't covered elsewhere?
|
||||
|
||||
That's a valid question. There are many good resources and examples already. First
|
||||
of all, this book will point you to some background information that I have found
|
||||
very valuable, especially `Generators` and stackless coroutines.
|
||||
of all, this book will focus on `Futures` and `async/await` specifically and
|
||||
not in the context of any specific runtime.
|
||||
|
||||
I find that many discussions arise, not because `Futures` is a hard concept to
|
||||
grasp, but that concurrent programming is a hard concept in general.
|
||||
|
||||
Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||
Secondly, I've always found small runnable examples very exiting to learn from.
|
||||
Thanks to Mdbook the examples can even be edited and explored further. It's
|
||||
all code that you can download, play with and learn from.
|
||||
|
||||
## What we'll do and not
|
||||
We'll and end up with an understandable example including a `Future`
|
||||
implementation, an `Executor` and a `Reactor` in less than 200 lines of code.
|
||||
We don't rely on any dependencies or real I/O which means it's very easy to
|
||||
explore further and try your own ideas.
|
||||
|
||||
**We'll:**
|
||||
|
||||
- Implement our own `Futures` and get to know the `Reactor/Executor` pattern
|
||||
- Implement our own waker and learn why it's a bit foreign compared to other types
|
||||
- Talk a bit about runtime complexity and what to keep in mind when writing async Rust.
|
||||
- Make sure all examples can be run on the playground
|
||||
- Not rely on any helpers or libraries, but try to face the complexity and learn
|
||||
|
||||
**We'll not:**
|
||||
|
||||
- Talk about how futures are implemented in Rust the language, the state machine and so on
|
||||
- Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||
better off if you read this before you go research them
|
||||
- Explain concurrent programming, but I will supply sources
|
||||
|
||||
I do want to explore Rusts internal implementation but that will be for a later
|
||||
book.
|
||||
|
||||
## Credits and thanks
|
||||
|
||||
I'll like to take the chance of thanking the people behind `mio`, `tokio`,
|
||||
`async_std`, `Futures`, `libc`, `crossbeam` and many other libraries which so
|
||||
much is built upon. Reading and exploring some of this code is nothing less than
|
||||
impressive.
|
||||
|
||||
## Why is `Futures` in Rust hard to understand
|
||||
|
||||
Well, I think it has to do with several things:
|
||||
|
||||
1. Futures has a very interesting implementation, compiling down to a state
|
||||
machine using generators to suspend and resume execution. In a language such as
|
||||
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||
if this complexity when working with futures and want to understand them, not
|
||||
only learn how to use them.
|
||||
|
||||
2. Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||
yourself and actually know what a `Reactor` and an `Executor` is. While not
|
||||
too difficult, you need to make more choices than you need in GO and other
|
||||
languages designed with a concurrent programming in mind and ships with a
|
||||
runtime.
|
||||
|
||||
3. Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||
known to have some issues regarding ergonomics. Turns out that modelling
|
||||
async coding after `Promises` in JavaScript can turn in to extremely long errors
|
||||
and type signatures with a type system as Rust.
|
||||
|
||||
Futures 3.0 are not compatible with Futures 1.0 without performing some work.
|
||||
|
||||
4. Async await syntax was recently stabilized
|
||||
|
||||
what we'll
|
||||
really do is to stub out a `Reactor`, and `Executor` and implement
|
||||
impressive. Even the RFCs that much of the design is built upon is written in a
|
||||
way that mortal people can understand, and that requires a lot of work. So thanks!
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,75 @@ information that will help demystify some of the concepts we encounter.
|
||||
Actually, after going through these concepts, implementing futures will seem
|
||||
pretty simple. I promise.
|
||||
|
||||
## Async in Rust
|
||||
|
||||
Let's get some of the common roadblocks out of the way first.
|
||||
|
||||
Async in Rust is different from most other languages in the sense that Rust
|
||||
has an extremely lightweight runtime.
|
||||
|
||||
In languages like C#, JavaScript, Java and GO, the runtime is already there. So
|
||||
if you come from one of those languages this will seem a bit strange to you.
|
||||
|
||||
### What Rust's standard library takes care of
|
||||
|
||||
1. The definition of an interruptible task
|
||||
2. An extremely efficient technique to start, suspend, resume and store tasks
|
||||
which are executed concurrently.
|
||||
3. A defined way to wake up a suspended task
|
||||
|
||||
That's really what Rusts standard library does. As you see there is no definition
|
||||
of non-blocking I/O, how these tasks are created or how they're run.
|
||||
|
||||
|
||||
### What you need to find elsewhere
|
||||
|
||||
A runtime. Well, in Rust we normally divide the runtime into two parts:
|
||||
|
||||
- The Reactor
|
||||
- The Executor
|
||||
|
||||
Reactors create leaf `Futures`, and provides things like non-blocking sockets,
|
||||
an event queue and so on.
|
||||
|
||||
Executors, accepts one or more asynchronous tasks called `Futures` and takes
|
||||
care of actually running the code we write, suspend the tasks when they're
|
||||
waiting for I/O and resumes them.
|
||||
|
||||
In theory, we could choose one `Reactor` and one `Executor` that have nothing
|
||||
to do with each other besides one creates leaf `Futures` and one runs them, but
|
||||
in reality today you'll most often get both in a `Runtime`.
|
||||
|
||||
There are mainly two such runtimes today [async_std][async_std] and [tokio][tokio].
|
||||
|
||||
Quite a bit of complexity attributed to `Futures` are actually complexity rooted
|
||||
in runtimes. Creating an efficient runtime is hard. Learning how to use one
|
||||
correctly can be hard as well, but both are excellent and it's just like
|
||||
learning any new library.
|
||||
|
||||
The difference between Rust and other languages is that you have to make an
|
||||
active choice when it comes to picking a runtime. Most often you'll just use
|
||||
the one provided for you.
|
||||
|
||||
## Futures 1.0 and Futures 3.0
|
||||
|
||||
I'll not spend too much time on this, but it feels wrong to not mention that
|
||||
there have been several iterations on how async should work in Rust.
|
||||
|
||||
`Futures 3.0` works with the relatively new `async/await` syntax in Rust and
|
||||
it's what we'll learn.
|
||||
|
||||
Now, since this is rather recent, you can encounter creates that use `Futures 1.0`
|
||||
still. This will get resolved in time, but unfortunately it's not always easy
|
||||
to know in advance.
|
||||
|
||||
A good sign is that if you're required to use combinators like `and_then` then
|
||||
you're using `Futures 1.0`.
|
||||
|
||||
While not directly compatible, there is a tool that let's you relatively easily
|
||||
convert a `Future 1.0` to a `Future 3.0` and vice a verca. You can find all you
|
||||
need in the [`futures-rs`][futures_rs] crate and all [information you need here][compat_info].
|
||||
|
||||
## First things first
|
||||
|
||||
If you find the concepts of concurrency and async programming confusing in
|
||||
@@ -29,4 +98,9 @@ learn are:
|
||||
2. Generators/stackless coroutines
|
||||
3. Pinning, what it is and why we need it
|
||||
|
||||
Let's get moving!
|
||||
Let's get moving!
|
||||
|
||||
[async_std]: https://github.com/async-rs/async-std
|
||||
[tokio]: https://github.com/tokio-rs/tokio
|
||||
[compat_info]: https://rust-lang.github.io/futures-rs/blog/2019/04/18/compatibility-layer.html
|
||||
[futures_rs]: https://github.com/rust-lang/futures-rs
|
||||
Reference in New Issue
Block a user