refactored intro and first chapter

This commit is contained in:
Carl Fredrik Samson
2020-01-31 20:45:55 +01:00
parent b180e1aa2c
commit a84ec7d6c8
2 changed files with 91 additions and 60 deletions

View File

@@ -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!

View File

@@ -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