This commit is contained in:
Carl Fredrik Samson
2020-01-28 21:35:09 +01:00
parent f23d660289
commit cbe1430223
2 changed files with 23 additions and 12 deletions

View File

@@ -53,17 +53,17 @@ let future = Connection::connect(conn_str).and_then(|conn| {
let rows: Result<Vec<SomeStruct>, SomeLibraryError> = block_on(future).unwrap(); let rows: Result<Vec<SomeStruct>, SomeLibraryError> = block_on(future).unwrap();
``` ```
While an effective solution there are mainly two downsides I'll focus on: While an effective solution there are mainly three downsides I'll focus on:
1. The error messages produced could be extremely long and arcane 1. The error messages produced could be extremely long and arcane
2. Not optimal memory usage 2. Not optimal memory usage
3. Did not allow to borrow across combinator steps. 3. Did not allow to borrow across combinator steps.
#3, is actually a major drawback with `Futures 1.0`, there were no way to borrow Point #3, is actually a major drawback with `Futures 1.0`.
data across callbacks in Rust.
This ends up being very un-ergonomic and often requiring extra allocations or Not allowing borrows across suspension points ends up being very
copying to accomplish some tasks which is inefficient. un-ergonomic and often requiring extra allocations or copying to accomplish
some tasks which is inefficient.
The reason for the higher than optimal memory usage is that this is basically The reason for the higher than optimal memory usage is that this is basically
a callback-based approach, where each closure stores all the data it needs a callback-based approach, where each closure stores all the data it needs
@@ -72,19 +72,29 @@ the needed state increases with each added step.
### Stackless coroutines/generators ### Stackless coroutines/generators
This is the model used in Async/Await today. It has two advantages: This is the model used in Rust today. It a few notable advantages:
1. It's easy to convert normal Rust code to a stackless corotuine using using 1. It's easy to convert normal Rust code to a stackless corotuine using using
async/await as keywords (it can even be done using a macro). async/await as keywords (it can even be done using a macro).
2. No need for context switching and saving/restoring CPU state 2. No need for context switching and saving/restoring CPU state
3. No need to handle dynamic stack allocation 3. No need to handle dynamic stack allocation
4. It uses memory very efficiently 4. Very memory efficient
5. Allowed for borrows across suspension points
The second point is in contrast to `Futures 1.0` (well, both are efficient in The last point is in contrast to `Futures 1.0`. With async/await we can do this:
practice but thats beside the point). Generators are implemented as state
machines. The memory footprint of a chain of computations is only defined by ```rust
the largest footprint any single step requires. That means that adding steps to async fn myfn() {
a chain of computations might not require any added memory at all. let text = String::from("Hello world");
let borrowed = &text[0..5];
}
```
Generators are implemented as state machines. The memory footprint of a chain
of computations is only defined by the largest footprint any single step
requires. That means that adding steps to a chain of computations might not
require any added memory at all.
## How generators work ## How generators work

View File

@@ -15,3 +15,4 @@ try to give a high level overview that will make it easier to learn Rusts
* [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html) * [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html)
* [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html) * [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html)
r