# Async Fundamentals Mini Vision Doc
# Phase 1
Grace and Alan are working at BoggleyCorp, developing a network service using async I/O in Rust. Internally, they use a trait to manage http requests, which allows them to easily change to different sorts of providers:
```rust
trait HttpRequestProvider {
async fn request(&mut self, request: Request) -> anyhow::Result<Response>;
}
```
They start out using `impl HttpRequest` and things seem to be working very well:
```rust
async fn process_request(
mut provider: impl HttpRequestProvider,
request: Request,
) -> anyhow::Result<()> {
let response = provider.request(request).await?;
process_response(response).await;
}
```
As they refactor their system, though, they decide they would like to store the provider in a `Context` type. When they create the struct, though, they realize that `impl Trait` syntax doesn't work in that context:
```rust
struct Context {
provider: impl HttpRequestProvider,
}
async fn process_request(
mut provider: impl HttpRequestProvider,
request: Request,
) -> anyhow::Result<()> {
let cx = Context {
provider
};
// ...
}
```
Alan looks to Grace, "What do we do now?" Grace says, "Well, we could make an explicit type parameter, but I think a `dyn` might be easier for us here." Alan says, "Oh, ok, that makes sense." He alters the struct to use a `Box<dyn HttpRequestProvider>`:
```rust
struct Context {
provider: Box<dyn HttpRequestProvider>,
}
```
However, this gets a compilation error: "traits that contain async fn are not dyn safe". The compiler does, however, suggest that they check out the experimental [`dyner` crate](https://github.com/nikomatsakis/dyner). The README for the crate advises them to decorate their trait with `dyner::make_dyn`, and to give a name to use for the "dynamic dispatch" type:
```rust
#[dyner::make_dyn(DynHttpRequestProvider)]
trait HttpRequestProvider {
async fn request(&mut self, request: Request) -> anyhow::Result<Response>;
}
```
Following the readme, they also modify their `Context` struct like so:
```rust
struct Context {
provider: DynHttpRequestProvider<'static>,
}
async fn process_request(
provider: impl HttpRequestProvider,
request: Request,
) -> anyhow::Result<()> {
let cx = Context {
provider: DynHttpRequestProvider::from(provider),
};
// ...
}
```
However, the code doesn't compile just as is. They realize that the `Context` is only being used inside `process_request` and so they follow the "time-limited" pattern of adding a lifetime parameter `'me` to the context. This represents the period of time in which the context is in use:
```rust
struct Context<'me> {
provider: DynHttpRequestProvider<'me>,
}
async fn process_request(
provider: impl HttpRequestProvider,
request: Request,
) -> anyhow::Result<()> {
let cx = Context {
provider: DynHttpRequestProvider::from(provider),
};
// ...
}
```
At this point, they are able to invoke `provider.request()` as normal.
### Spawning tasks
As their project expands, Alan and Grace realize that they are going to have process a number of requests in parallel. They insert some code to spawn off tasks:
```rust
fn process_all_requests(
provider: impl HttpRequestProvider + Clone,
requests: Vec<RequestData>,
) -> anyhow::Result<()> {
let handles =
requests
.into_iter()
.map(|r| {
let provider = provider.clone();
tokio::spawn(async move {
process_request(provider, r).await;
})
})
.collect();
tokio::join_all(handles).await
}
```
However, when they write this, they get an error:
```
error: future cannot be sent between threads safely
--> src/lib.rs:21:17
|
21 | tokio::spawn(async move {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
note: captured value is not `Send`
--> src/lib.rs:22:37
|
22 | process_request(provider, r).await;
| ^^^^^^^^ has type `impl HttpRequestProvider + Clone` which is not `Send`
note: required by a bound in `tokio::spawn`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.13.0/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
help: consider further restricting this bound
|
13 | provider: impl HttpRequestProvider + Clone + std::marker::Send,
| +++++++++++++++++++
```
"Ah, right," thinks Alan. "I need to add `Send` to show that the provider is something we can send to another thread."
```rust
async fn process_all_requests(
provider: impl HttpRequestProvider + Clone + Send,
// ^^^^ added this
requests: Vec<Request>,
) {
...
}
```
But when he adds that, he gets *another* error. This one is a bit more complex:
```
error[E0277]: the future returned by `HttpRequestProvider::request` (invoked on `impl HttpRequestProvider + Clone + Send`) cannot be sent between threads safely
--> src/lib.rs:21:17
|
21 | tokio::spawn(async move {
| ^^^^^^^^^^^^ `<impl HttpRequestProvider + Clone + Send as HttpRequestProvider>::Request` cannot be sent between threads safely
|
note: future is not `Send` as it awaits another future which is not `Send`
--> src/lib.rs:35:5
|
35 | let response = provider.request(request).await?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ this future is not `Send`
note: required by a bound in `tokio::spawn`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.13.0/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
12 ~ async fn process_all_requests<H: HttpRequestProvider + Clone + Send>(
13 ~ provider: H,
14 | requests: Vec<Request>,
15 ~ ) where <<H as HttpRequestProvider>::fn#request as FnOnce>::Output: std::marker::Send {
|
```
Alan and Grace are a bit puzzled. They decide to to their friend Barbara, who knows Rust pretty well.
### Barbara explains function types
Barbara looks over the error. She explains to them that when they call an async function -- even in a trait -- that results in a future. "Yeah, yeah", they say, "we know that." Barbara explains that this means that saying that `provider` is `Send` does not necessarily imply that the future resulting from `provider.request()` is `Send`. "Ah, that makes sense," says Alan. "But what is this suggestion at the bottom?"
"Well," asks Barbara, "do you know about the zero-sized types Rust has for each function?" Alan and Grace give her a blank stare. "OK," she says, "take a look at this example, where we store the value of the function `foo` into a variable `f`.":
```rust
fn foo() { }
fn main() {
let f = foo;
println!("{}", std::mem::size_of_val(&f));
f();
}
```
"What do you think is the type of `f`? You might think it's the type `fn()`, but that's not right. If you run this program, you will see that it prints 0. That is because every function in Rust has a corresponding function type. This type has size 0 and it implements the Rust closure traits, like `FnMut`, and when you call the function, you are actually calling the `FnMut::call_mut` trait method on a value of this special type."
Alan asks, "Does that type have a name?"
"Yes!" answers Barbara, "it is written `fn#foo`, where `foo` is the name of the function. We could rewrite that example with an explicit type like so:"
```rust
fn foo() { }
fn main() {
let f: fn#foo = foo;
println!("{}", std::mem::size_of_val(&f));
f();
}
```
"Hmm," says Grace, "I thought the type of `f` was `fn()`."
"A common misconception," syas Barbara. "`fn()` is actually the type of a function pointer. You can coerce from a function type like `fn#foo` to a function pointer. At that point, though, the type isn't unique to a specific function, so the compiler doesn't know what is being called. Therefore, you will see that the size is now 8. This is because, at runtime, we have to carry a pointer to the function so we can call it:"
```rust
fn foo() { }
fn main() {
let f: fn() = foo;
// ^^^^ coercion here
println!("{}", std::mem::size_of_val(&f)); // Prints 8
f();
}
```
"OK," says Alan, "but what does that have to do with our error?"
Barbara explains: "The compilers wants to name the return type of `provider.request()`, but because `request` is a trait method, we don't know exactly what function is being called. In other words, in our little example, we could write `fn#foo` to talk about `foo`, but here we want "the type of the `request` method for `H`. The trick is that, for every method in the trait, there is also a corresponding associated type with its type. That type is named `fn#xxx`, where `xxx` is the name of the method. In this case, that should be `fn#request`, so the compiler is trying to name that type for you." She grabs her keyboard and starts to type. "Look. Here is the what the error message suggested:"
```
<<H as HttpRequestProvider>::fn#request as FnOnce>::Output: std::marker::Send
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
// Type of the `fn#request` method Return type from the `FnOnce` trait.
// for H.
```
"Ah, I see," says Alan. "That makes sense, but it's a mouthful to type!"
"Yeah," says Barbara. "That's true, it's kind of a pain. The plan is to add something more concise, but nobody has quite figured out what that should be yet. For now, we only have this very explicit form. You should add a comment to the tracking issue! They're interested to know how often this scenario crops up in practice."
### Dyner all the things (even non-async things)
As Alan and Grace get used to dyner, they start using it for all kinds of dynamic dispatch, including some code which is not async. It takes getting used to, but dyner has some definite advantages over the builtin Rust functionality, particularly if you use it everywhere. For example, you can use `dyner` with traits that have `self` methods and even methods which take and return `impl Trait` values (so long as those traits also use dyner, and they use the standard naming convention of prefixing the name of the trait with `Dyn`):
```rust
// The dyner crate re-exports standard library traits, along
// with Dyn versions of them (e.g., DynIterator). You do have
// to ensure that the `Iterator` and `DynIterator` are reachable
// from the same path though:
use dyner::iter::{Iterator, DynIterator};
#[dyner::make_dyn(DynWidgetStream)]
trait WidgetTransform {
fn transform(
mut self,
// ^^^^ not otherwise dyn safe
w: impl Iterator<Item = Widget>,
// ^^^^^^^^^^^^^ this would not ordinarily be dyn-safe
) -> impl Iterator<Item = Widget>;
// ^^^^^^^^^^^^^ this would not ordinarily be dyn-safe
}
```
### Dyn trait objects for third-party traits
Using `dyner`, Alan and Barbara are basically unblocked. After a while, though, they hit a problem. One of their dependencies defines a trait that has no `dyn` equivalent (XXX realistic example?):
```rust
// In crate parser
trait Parser {
fn parse(&mut self, input: &str);
}
```
They are able to manage this by declaring the "dyn" type themselves. To do so, however, they have to copy and paste the trait definition, which is kind of annoying:
```rust
mod parser {
dyner::make_dyn_extern {
trait parser::Parser {
fn parse(&mut self, input: &str);
}
}
}
```
They can now use `crate::parser::{Parser, DynParser}` to get the trait and its Dyn equivalent; the `crate::parser::Parser` is a re-export. "Ah, so that's how dyner re-exports the traits from libstd", Grace realizes.
### What's missing?
* Spawning and needing `Send` bounds
* Appendix: how does dyner work?
### XXX
* replacement for `where Self: Sized`?
* a lot of those problems go away *but*
* we can also offer an explicit `#[optimization]` annotation:
* causes the default version to be reproduced for dyn types and exempts the function from all limitations
* inherent async fn
* `impl A + B`
* `DynerAB`-- we could even do that, right?
*
* https://docs.rs/hyper/0.14.14/hyper/body/trait.HttpBody.html