# Async Fundamentals Ahoy
## Current sprint goals
- [ ] (nikomatsakis) Post RFC for Associated Return Types / Return Type Notation
- [ ] (tmandry) Respond to RPITIT RFC comments
- [ ] (tmandry) Write a high-level plan for async iterators
* RPITIT `Self` question
* Edition-related change for `impl Trait` after `->`?
## 2023-09-01
Stabilization: Goal is to land in October.
RPITIT captures and dyn
* It would be nice if we had an explicit syntax for outlives in `dyn`, and if we had that we could also use it for `impl`
* `dyn<'a, T> Debug`
* `impl<'a, T> Any`
```rust
fn foo(x: &i32) -> Box<dyn Debug> {}
let y: i32 = 6;
let x: Box<dyn<'_> Debug> = foo(&y);
struct Foo<'x> {
x: Box<dyn Debug>,
}
fn method(x: Box<dyn Debug>)
```
## 2023-06-09
sprint ends...? month
* RPITIT / async fn stabilization
* https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html
* one part is technical
* one part is messaging it
* "bad error messages"
* testing status?
* async fn desugaring, is it the same as everything else?
* June update
* prerelease ?
* alpha channel ?
* something that lets us get these features out more
* RTN
* `()` as sugar for `Output` at some point
* Why start here?
* `()` is friendlier, avoids confusion
* Also:
* `Output` is not a "simple building block"
* Action items
* June blog post
* call for testing on RPITIT, async fn
* compiler-errors to triage problems with RPITIT
* implement `#[refine]`-compatible-subset feature gate
* check with mgx on status
* RTN
* tmandry: talk to Josh
* update the RFC
## 2023-06-02: sprint goals, oh my
* RTN RFC
* grow scope?
* allow in type position
* support top-level items
```rust
// what about "non-embedded" uses>
fn foo<HC: HealthCheck>()
where
HC::check(): Send,
Box<HC::check()>: Send
// -----------
// becomes
fn foo<HC: HealthCheck>()
where
for<'a> Box<<HC as HealthCheck>::check(&'a mut Self)>: Send
// what about this, where does binder go?
fn foo<HC: HealthCheck>()
where
fn(HC::check()): Send
// is this (for<'a> <HC as HealthCheck>::check(&'a mut Self)): Send
// or for<'a> (<HC as HealthCheck>::check(&'a mut Self): Send)
where
&T: Foo,
where
for<'a> &'a T: Foo,
```
things:
* behavior of `'_` in where-clauses (disallowed today, would become `for`) vs lets (allowed today, is existential)
* `HC: HealthCheck<check(): Send>` notation
* `HC::check(): Send`
* `Box<HC::check()>: Send`
* `let x: HC::check() = ...`
* `let x: foo() = ...`
* consistency
* `HC::check()` as a type expands to `<HC as HealthCheck>::check(&mut Self)`
* which in turn expands to however `'_` should expand
---
another feature: https://github.com/rust-lang/rust/issues/103532
* https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=79775d976a178f8dc9534e60e7e5518d
* https://github.com/rust-lang/rust/issues/112194
## 2023-05-19: impl Future / async fn and refine
### alternative "storyline"
* stabilize AFIT / RPITIT "MVP" ships
* don't stop using `async_trait` yet!
* this is enough for some users, but not all! lacks send bounds!
* (maybe we can come up with some proc macros??)
### "bounds" story
* `Output`
* `return`
* `T()`
---
proposal for `Output`
```rust!
trait Trait<T> {
type Output;
}
```
* user writes `where T::Output: Send`, when `T` has higher-ranked bounds, becomes a higher-ranked where-clause
* given `where for<'a> T: Trait<&'a u32>`
* today results in an error
* desugars to `where for<'a> <T as Trait<&'a u32>::Output: Send`
* `T: MyTrait<some_method::Output: Send>`
* let `some_method` be a type that implements the `Fn` trait
* `fn some_method(&u32) -> &u32` --
* `T::some_method: Fn(&u32) -> &u32` --
* `for<'a> T::some_method: Fn<(&'a u32,), Output = &'a u32>`
* `T::some_method::Output: Send`
* `for<'a> T::some_method: Fn<(&'a u32,),>::Output: Send`
* `for<'a> &'a u32: Send` (normalized)
* what niko encounters
* `where T::Output<'_>: Send` -- `for<'a> T::Output<'a>: Send`
---
proposal for `return` keyword
* `T::return`
* `T: Trait<my_method::return: Send>`
---
why not RTN
"doesn't look like a type"
some of the future possibilities like so seem confusing
```rust
fn foo(x: u32) { }
let x: foo() = foo(u32);
// ^^ no arguments?
```
but we *try* to make types *look* like their constructor patterns normally?
```rust
const fn foo() -> u32 { }
T<foo()> // parameterized by fn type
T<{foo()}> // parameterized by a constant, the result of calling fn
```
"but isn't `T::return` and `T::Output` super verbose?"
yeah but `()` isn't better enough, people are going to want trait aliases and so on anyway
`Trait<method::Output: Send>`
`Trait<method(): Send>`
"yagni"
when do you to specify the parameters types?
* generic type parameters --
* region parameters --
* impl Trait in argument position -- would rather fix by allowing impl trait to be turbofishable
* feels like letting perfect be enemy of god?
what niko doesn't like
* repetitive to repeat "fixed" parts of the type
* but could use `_`
but RTN didn't commit to specifying parameter types, so all of this seems a bit beyond the point
```rust!
fn trait<HC>(hc: &mut HC)
where
HC: HealthCheck<check().await: Debug>
```
## 2023-05-15: impl Future / async fn and refine
Some notes about `impl Future` and async fn overlap.
Goal is to have `async fn` and `-> impl Future` be interoperable, right? But how is it ok for them to vary? The details of async fn desugaring are fiddly.
If we assume the rules given in the RFC:
* if `impl Bounds1` is used in trait (directly or indirectly via async fn):
* `impl Bounds2` must be used in impl (directly or indirectly via async fn)
* let `H` be the "hidden type" derived from the impl
* then `H: Bounds1`
* and `Bounds1 => Bounds2`
then a number of common scenarios don't work, for better or worse.
More expressive version is:
* (hidden) value actually returned by impl meets the bounds in the trait
* this allows for auto trait leakage, in particular
* for any value matched by the impl, without knowing hidden type, we can use bounds on trait to prove bounds on impl
* "impl doens't promise stronger bounds than trait"
### Strengthening via outlives bounds
In this case, we strengthen via outlives bounds. The impl meets the trait requirements (and then some), but it would be illegal under terms of the RFC. In particular, assuming only what the trait gives you, you cannot prove that the `'_` bound holds.
```rust
trait AsyncProvider {
async fn provide(&self) -> Value;
}
impl AsyncProvider for MyType {
fn provide(&self) -> impl Future<Output = Value> + '_ {
async move { Value::new() }
}
}
```
What about `Captures`?
```rust
trait AsyncProvider {
async fn provide(&self) -> Value;
}
impl AsyncProvider for MyType {
fn provide(&self) -> impl Future<Output = Value> + Captures<&'_()> {
async move { self.provide() }
}
}
trait Captures<T: ?Sized> { }
impl<A: ?Sized, B: ?Sized> Captures<B> for A { }
```
Presuming the check is done *semantically*, this is a faithful desugaring and is accepted.
### Trait uses outlives, impl hidden type meets
Inverse situation:
```rust
trait AsyncProvider {
fn provide(&self) -> impl Future<Output = Value> + '_;
}
impl AsyncProvider for MyType {
async fn provide(&self) {
async move { Value::new() }
}
}
```
This should pass under the algorithm above. You can prove `'_` by knowing the `async fn`.
### Trait guarantees Send, impl hidden type provides
Same situation as before, but with `Send`. The hidden types are `Send` but the declared return type is not. Without `#[refine]`, this would be an error or at least a warning.
```rust
trait AsyncProvider {
fn provide(&self) -> impl Future<Output = Value> + Send + '_;
}
impl AsyncProvider for MyType {
async fn provide(&self) -> Value {
Value::new()
}
//or
fn provide(&self) -> impl Future<Output = Value> {
async move { Value::new() }
}
}
```
```rust
impl AsyncProvider for MyType {
type provide<'_> = impl Future<Output = Value> + Send;
// impl Future<Output = Value>
fn provide(&self) -> self::provide<'_> {
async move { Value::new() }
}
}
```
```rust
impl AsyncProvider for MyType {
fn provide(&self) -> Foo {
async move { Value::new() }
}
}
```
### Default example
### exploring as we talk
alternative formulation of the refine check
```rust
trait SomeTrait<A> {
fn some_method(&self) -> impl Bounds1<A>;
}
impl<T, U> SomeTrait<Y<U>> for X<T>
where
T: Ord,
{
fn some_method(&self) -> impl Bounds2;
}
struct X<T: Ord> { }
struct Y<T> { }
```
* make placeholders for all early-bound parameters on impl/method and all late-bound parameters on the method
* form trait substition: `X<!T>: SomeTrait<Y<!U>>`
* let P = projection-y type `<X<!T> as SomeTrait<Y<!U>>::Dummy<'!_>`
* ImplWhereClauses =>
* Bounds2(P) =>
* [subst] Bounds1(P)
* forall T, U, '_
* T: Ord =>
* `Dummy<X<T>, Y<U>, '_>: Bounds2` =>
* `Dummy<X<T>, Y<U>, '_>: Bounds1<Y<U>>`
### one way doors
* people can do -- basically if you have only one lifetime
* `-> impl Future + '_`
* `-> impl Future + Send + '_`
* can do it by writing an `async fn` and cfg'ing nightly
* if people author an impl trait with excessive bounds, they may not be able to move to `async fn` later
### send + RTN alias
```rust
trait LocalFoo {
async fn next1();
async fn next2();
}
trait Foo: LocalFoo<next1(): Send, next2(): Send> { }
impl<T: LocalFoo<next1(): Send, next2(): Send> Foo for T { }
```
### another interesting thing
```rust
trait Foo {
async fn next1() where Self::next1(): Send;
// conceptually similar to -> impl Future + Send
async fn next2();
}
```
```rust
trait Foo where Self::next1(): Send {
async fn next1();
// conceptually similar to -> impl Future + Send
async fn next2();
}
```
## 2023-05-12
### RPITIT
* we have GATs, let's add TAITs and see if it's good enough -- no sugar needed
* details of refine + encapsulation
* can we think of this as desugaring to an associated type
* (a) impl complexity
* (b) forward compat story for `impl Future` etc
* tangent: impl Trait capture rules
* "is this RFC complete without a solution to the send bounds problem"
```rust
trait Provider {
// fn provide(&self) -> impl Future<Output = Credentials> + Send + '_;
type provide<'_>: Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_>;
}
// Unrefined impl
impl Provider for MyThing {
// fn provide(&self) -> impl Future<Output = Credentials> + Send + '_ { }
type provide<'_> = impl Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_> {
... // something Send
}
// async fn provide(&self) -> Credentials { } // becomes
// fn provide(&self) -> impl<'_> Future<Output = Credentials> { async move { .. } }
type provide<'_> = impl Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_> {
async move { ... } // something Send
}
// fn provide(&self) -> MyFuture { }
type provide<'_> = impl Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_> {
(...): MyFuture // something Send
}
}
// Refine impl
impl Provider for MyThing {
// #[refine]
// fn provide(&self) -> impl Future<Output = Credentials> + Send + '_ { }
type provide<'_> = impl Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_> {
... // compiles
}
// #[refine]
// async fn provide(&self) -> Credentials { async move { .. } } // becomes
// fn provide(&self) -> impl<'_> Future<Output = Credentials> { }
type provide<'_> = impl Future<Output = Credentials> + '_;
fn provide(&self) -> Self::provide<'_> {
async move { ... } // compiles if Send because leakage
}
// #[refine]
// fn provide(&self) -> MyFuture { }
type provide<'_> = MyFuture;
fn provide(&self) -> Self::provide<'_> {
... // compiles iff MyFuture: Send
}
}
```
why users might want to write impls w/ refined signatures
```rust
struct MyType { f: u32 }
impl From<&u32> for MyType {
fn from(v: &u32) -> MyType {
MyType { f: *v }
}
}
impl<'a> From<&'a u32> for MyType {
fn from<'b>(v: &'b u32) -> MyType {
MyType { f: *v }
}
}
```
```rust
trait Twisted<'a> {
fn twisted<T>(self, f: &'a u32) -> T
where
T: SomeTrait<Self>;
}
impl SomeTrait<u32> for u32 { }
impl Twisted<'_> for u32 {
fn twisted<T>(self, f: &u32) -> T
where
T: SomeTrait<Self>
{ panic!() }
}
impl SomeTrait<i32> for i32 { }
impl<'a> Twisted<'a> for i32 {
fn twisted<T>(self, f: &'a u32) -> T
where
T: SomeTrait<Self>
{ panic!() }
}
fn main() {
let x: u32 = Default::default().twisted(&22);
}
```
```rust
trait SomeTrait<T> { }
trait Twisted<'a> {
fn twisted<T>(&'a mut self, f: &'a T)
where
T: SomeTrait<Self>;
}
impl SomeTrait<u32> for u32 { }
impl Twisted<'_> for u32 {
fn twisted<T>(&'a mut self, f: &T)
where
T: SomeTrait<Self>
{ panic!() }
}
impl SomeTrait<i32> for i32 { }
impl<'a> Twisted<'a> for i32 {
fn twisted<T>(&'a mut self, f: &'a T)
where
T: SomeTrait<Self>
{ panic!() }
}
fn main() {
let mut v = Default::default();
v.twisted(&22_u32);
}
```
```rust
trait Provider {
fn provide(&self) -> impl Future<Output = Credentials> + Send + '_;
}
impl Provider for MyThing {
type provide<'_> = impl Future<Output = Credentials> + Send + '_;
fn provide(&self) -> Self::provide<'_> {
... // something Send
}
}
```
```rust
trait Provider {
fn provide(&self) -> impl Future<Output = Credentials> + Send + '_;
}
impl Provider for MyThing {
async fn provide(&self) -> Credentials {
... // something Send
}
}
```
### Send bounds problem
* RTN
* `T::Output`
* `T::return`
## 2023-05-05
### select patterns
decent ways to use select
#### task pattern
moro makes this ergonomic, but you can do it by hand
```rust
async fn foo(rx: Receiver<T>) -> io::Result<()> {
while let Some(item) = rx.recv().await {
...
}
}
async fn bar(rx: Receiver<T>) -> io::Result<()> {
while let Some(item) = rx.recv().await {
...
}
}
// no loop here
select! {
res = foo(rx1) => res?,
res = bar(rx2) => res?,
}
```
tasks work great with disjoint data
but when you have a lot of shared data, kind of annoying, because you need mutexes
#### merge pattern
```rust
for event in foo(rx1).merge(bar(rx2)) {
match event {
...
}
}
```
### actors
Have elements of both. Merge pattern for mailbox.
In Erlang, the pattern is to have one mailbox per actor and then recursively wait on the mailbox for _certain_ messages while processing those, queueing up other messages in the meantime.
In Rust we'd probably make a separate mailbox. (Or handle responses separately.)
### "pollable" concept
Definition: Object that has (potentially multiple) ongoing operations, but keeps all the state for those operations internally.
Futures made for these objects don't have state, they just check the internal state of the object. This makes them "cancel safe".
Select makes sense if all your futures are from pollable things.
Hard to make new pollable things, but easy to work with them.
### generators
```rust!
gen foo() -> i32 { ... }
fn bar() -> impl Iterator<Item = u32> {
// imagine that `foo().pinned()` is kind of `pin!(foo())`
foo().pinned().map(|i| i as u32) // ERROR
}
fn bar() -> impl Generator<Item = u32> {
foo().map(|i| i as u32)
}
trait Generator {
type Item;
fn into_iter(self) -> pin impl Iterator<Item = Self::Item>;
}
```
Generator is effectively IntoIterator with a pin requirement. (Not talking about current Generator trait which is a coroutine)
### impl trait captures
lcnr comment:
* RPITIT doesn't stand alone without ART (which they don't like)
* "into iterator" return type bounded in various places
* more conservative to use an explicit associated type and say what defines it
* if you look at the ergonomics improvement by RPITIT, you could do it with a proc macro atop the more conservative option
```rust
trait Foo {
type SomeJavaMethod<A1, A2>;
fn some_java_method(&self, a1: A1, a2: A2) -> Self::SomeJavaMethod<A1, A2>
where
A1: IntoJava<Object>,
A2: IntoJava<String>;
}
trait Foo {
fn some_java_method(&self, a1: impl IntoJava<Object>, a2: impl IntoJava<String>) -> impl IntoRust<HashMap>;
}
```
### RPITIT etc
## 2023-04-28
Updates:
* Blog post draft: https://hackmd.io/J1I12HS_ToWr3gN4xLVoqA
* RPITIT RFC: https://github.com/rust-lang/rfcs/pull/3425
Not yet written or finished:
* rtn rfc / art rfc
* async closures blog post
* roadmap 2024
```rust
// Situation: You want to migrate from -> impl Trait to explicit associated type.
trait Foo {
fn foo(&self) -> impl Trait;
}
// This should be equivalent, non-breaking.
trait Foo {
type Bar = impl Trait; /* Self::foo() */
fn foo(&self) -> Self::Bar;
}
dyn Foo
// means:
dyn Foo<Bar/*[ = impl Trait]*/>
// which becomes:
dyn Foo<Bar = dyn* Trait>
```
## 2023-04-21
Publication plan and documents
* Blog post: 2023 first
* tmandy started
* https://hackmd.io/4GC4u79iRsO4485wx2kCZA
* draft this weekend?
* up by end of next week?
* RPITIT RFC
* tmandry + mgx
* https://hackmd.io/h9Cr4dfKR1KXC6P1lgM2cQ
* up by end of next week?
* RTN RFC
* https://hackmd.io/KJaC_dhZTmyR_Ja9ghdZvg
* Niko proposes reframing as "associated return types"
*
```rust
fn foo(x: impl Foo(u32): Send)
// probably equivalent to this
fn foo(x: impl Foo(u32) -> impl Send)
// this is really an associated type bound sugar
```
## 2023-04-07
Things to work on now
* RTN RFC
* Plan for closures
* Plan for iterators
Plan
* Blog post: 2023 first
With respect to closures: can we avoid full-on HKT by....
> You could allow `impl Fn(&T) -> impl Trait + '_` without giving users full HKT in the surface syntax. Either you implement just enough HKT infrastructure to support that or maybe you compile it a different way like a higher ranked existential (got this from a Reddit user). I don't think the impl trait as higher order function output type has the type inference problems that general HKT had.
Can we make `Fn*::Output<'a>` a GAT and use intersection lifetimes?
`impl Fn(&T) -> impl <Bounds>` in argument position desugars to:
`impl Fn(&T, Output: <Bounds>)`
```rust
fn method(impl OtherTrait<&T, Output: B>)
```
```rust
fn method<T>(t: T)
where
T: for<'a> OtherTrait<&'a T, Output: Future + 'a>
```
```rust
trait HazGat {
type GAT<'a>;
}
fn something()
where
T: HazGat,
{
let x: T::GAT = ...;
}
```
```rust
trait HazGat {
type GAT<'a = 'static>;
}
fn something()
where
T: HazGat,
{
let x: T::GAT = ...;
}
```
```rust
// V1
trait HazGat {
type GAT;
fn do_it(&mut self) -> Self::GAT;
}
fn something(t: T)
where
T: HazGat,
{
let x = t.do_it();
let y = t.do_it();
drop((x, y));
}
// V2
trait HazGat {
type GAT<'a>;
fn do_it<'a>(&'a mut self) -> Self::GAT<'a>;
}
fn something<X>(t: T)
where
T: HazGat<GAT<'_> = X>,
{
let x = t.do_it();
let y = t.do_it();
drop((x, y));
}
```
See also: https://internals.rust-lang.org/t/pre-rfc-backward-compatible-gatification/15141
might be able to get away with it for closures because for every `T: Fn` you can always normalize `T::Output`
* `T: Fn()` == `T: Fn<(), Output = ()>`
* `T: Fn() -> X` == `T: Fn<(), Output = X>`
* so we can have `T::Output<'a = 'static>` because we'll always be able to normalize `Outpu` by one step
* you could argue that this is usually the case for `Iterator` too, and maybe do something over an edition where plain `T: Iterator` becomes uglier
Conclusions?
* What we can't do: Have our cake and eat it too; change nothing and let everything be compatible with async
* you can make every `T: Iterator` keep its meaning and have a notation for "maybe lending" (e.g., `T: Iterator<Item<'_>>`)
* key generics becomes a kind of notation like this, or maybe it's a third option
* you can make every `T: Iterator` maybe lending (over an edition) and have a notation for "not lending" (e.g., `T: Iterator<Item<'_> = _>`)
* you need this to compile things that process multiple items at once
* we already force this "not lending" notation on the `Fn` trait because of its paren sugar and the fact that we never stabilized `<>` form
Things to write:
* Draft blog post with plans for 2023
* Draft blog post about closures
* Draft blog post about DAFIT
* RFC draft
* Draft blog post with plans for 2024 and beyond
## 2023-03-29
https://smallcultfollowing.com/babysteps/blog/2023/03/29/thoughts-on-async-closures/
Boats argument: a rephrasing
* Poll methods let you poll more than one thing at a time
* Async fns don't (without interior mutability). Could be a problem.
* But poll methods are mind bending.
* Also, storing iterator and future in the same struct.
* Footgun with poll: BBBS
* Some patterns e.g. with moro don't have this problem
* Footgun with async next: cancel-safety
Roadmap
* Drop portability in 2024
* Async iterators: Some trait in 2024
* We'll need to answer questions around generators, even if we don't ship them (stretch goal)
* Async closures are also required
* Probably wants some kind of async trait transformer (`async FnMut` as described in Niko's above post)
* maybe-async is a small step from that
* 2027 becomes all the problems we know we need to solve
Next steps
* Tyler to adapt recent roadmap doc to blog post
* Niko, Tyler to continue work on [RTN RFC](https://hackmd.io/KJaC_dhZTmyR_Ja9ghdZvg) on Friday
## 2023-03-21
Async closures
* Future that borrows from the closure: First attempted in [futures-rs#1023](https://github.com/rust-lang/futures-rs/pull/1023) with a more recent attempt in [this playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=b480eb854734a23aa525cf60dc71a40d)
* Idea is that you pass `pinned(T, F)` an argument and it passes back a `Pin<&mut T>` to that argument, which you can then use in the `F` closure and return a future containing it
* Doesn't currently work for closures, but does work for [named functions](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=8df290518b566408470001701ee2d816)
* cramertj: "fixing it is, I think, mostly not controversial-- it's just a bunch of little details that the vast majority of people hopefully never have to worry about"
* RTN PR https://github.com/rust-lang/rust/pull/109010
* FCP'd and moving to land
* Case studies
* Tower is in progress
* Embassy ?
* Async closures / async iterator discussion
* `fn foo<F>(x: impl Fn(&u32) -> F) where F: Future`
* lifetimes cannot be captured by F
* `fn foo(x: impl for<'a> Fn(&'a u32) -> Box<dyn Future + 'a>)` // explicitly
* works today but sucks (a) allocates (b) it's ergonomically awkward
* how could you do this without the box
* `-> impl Future`, but what does this desugar to?
* `fn foo(x: impl Fn(&u32) -> impl Future<Output = > + '_)`
* one way, HKT
* `fn foo<F<'_>>(x: impl for<'a> Fn(&u32) -> F<'a>) where for<'a> F<'a>: Future`
* alternative: a different trait that has a GAT
* `trait AsyncFn { type Output; type F<'a>: Future<Output = Self::Output> + 'a; fn call(&self) -> Self::F<'_>; }`, which is
* `trait AsyncFn { type Output; async fn call(&self) -> Self::Output; }`, which is
* `async Fn`
* question: can we view `async Fn` as a "desugared bound"
* yes, but it requires HKT, and it only works for fn, perhaps because it returns `Self::Output` directly (contrast with `Iterator`)
* we can imagine desuaring parenthesized syntax differently from `<>` syntax
* or maybe some kind of blanket impl to impl `async Fn` for any `Fn<Output: Future>`?
* Retro
* Sd: need a better forum for directing feedback
* Next steps, write-up MVPs
* Question A: Send bounds
* SAFIT + RTN (eholk? niko?)
* SAFIT + Send transformer (niko)
* SAFIT + tmandry special (tmandry)
* Question B: require async bounds ?
* case against (niko's blog post)
* case for?
* Blog post
* 2023: stabilize SAFIT
* Milestones:
* March: discuss two alternatives
* Both are available for experimentation on nightly (?)
* April: final decision via RFC
* *gain experience, fix bugs, polish, etc*
* July: stabilization
* 2024: iterator
* 2027: portability
```rust
async gen fn foo() -> u32 {
let v = vec![1, 2, 3];
for e in &v {
yield *e;
}
}
// you couldn't write this in an iterator *today*
// because iterator's don't require "pin"
//
// you could do `impl Iterator for Pin<impl DerefMut<Target = MyStruct>>`
// and do it
gen fn foo() -> u32 {
let v = vec![1, 2, 3];
for e in &v {
yield *e;
}
}
// not valid today ("lending iterator")
gen fn foo() -> &u32 {
let v = vec![1, 2, 3];
for e in &v {
yield e;
}
}
```
```rust
struct ManualFuture {
}
let x: impl async Trait;
let f: impl Future + 'static = async move {
let r = x.method().await;
// x is no longer borrowed
(r, x)
};
```
## 2023-03-13
Roadmap
* QQ?: Specification
* Niko/ehuss/Mara/Joel/Josh had a meeting
* Want to update RFC to talk about how it integrates with different team processes
* Needs a "rough draft"/outline
* Idea: RFCs get pared back; instead of stabilization PR you get a spec modification
* MIR formality RFC would overlap
* Can link to MIR formality for detailed algorithms etc.
* Questions:
* Include std?
* Whose job is it to update spec, can we take on "spec debt"?
* Who's the audience for the spec? (Consensus that it should include Rust users, which means we need a non-normative text like the reference.)
* Feedback from last meeting to consider
* (go through the doc)
* What is the MVP, two questions
* do we need the `async` keyword on trait
* should we use Send trait transformers or limited version of RTN?
* Factors to consider
* Uncertainty factor -- do we know what trait transformers and friends should look like?
* Time to delivery -- RTN is literally implemented already and readily spec'able
* "YAGNI" for RTN -- will we ever need this?
* Uncertainties
* what do we do with `async fn foo<T>()`?
* same question comes up for trait transformers
* what about self types?
* is there a similarly scoped down version of send transformers to consider?
* workarounds
* tmandry's magic proc-macro
* niko's proc-macro to derive an alias
* Zulip
* Alice: async Iterator and cancellation safety https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/Cancellation.20safety.20of.20AsyncIterator
* Nick: Thinks we need coarse-grained, unsure about fine-grained. Josh seems to agree.
* Niko's position: we can model with trait aliases; not great, but solid progress; do we know how Send should work exactly
* Include tooling concerns like Future sizes? Debugging?
* niko's take: no, this is a lang/lib roadmap
* encourage a tooling roadmap separately
* How to decide on Send bounds?
* April 5: Publish Inside Rust post on two Send bounds MVPs (or three, with the proc macro)
* April 12: Lang team meeting
* What to present on Thursday?
Niko's proposal
* 2023: Goal is minimal async trait
* Capabilities: static dispatch + send bounds
* By Apr 1: prepare three write-ups
* Min RTN (Niko will champion)
* Noncommittal deriver (Tyler will champion)
* other ideas people want to champion? e.g., min trait transformers
* By end of Apr:
* accepted RFC for one of these three approaches
* plus implemented
* May
* ...
* June
* ...
* By July:
* stabilized
* 2024: Goal is dyn dispatch + key library traits available
* Key library traits (roughly this priority order)
* Async Iterator
* Portability for protocol implements (e.g., hyper, quinn, s2n-quic)
* Async Read/Write
* Async Timer
* Async Spawn
* 2027: Async is delightful
* Some subset of
* Async drop
* Async main, async file + whatever APIs
* Global async runtime "switch" (and ideally a default value, so beginners don't have to think about this)
* Structured concurrency
* Cancellation protocols
* Other stuff
* Generators
* Await patterns
* Backtraces, tooling
* Questions for the group
* What do we need to do before we have stabilized?
* What should each write-up look like?
* Can we include a common piece of code, so we can readily compare? e.g., show how the AWS SDK looks?
* What do we need to be doing NOW to reach 2024 goals? 2027 goals?
## 2023-03-10
* Case studies
* AWS SDK case study https://github.com/rust-lang/async-fundamentals-initiative/pull/11
* Embassy case study
* Tower case study
* Fuchsia network stack will be using AFIT, case study?
* wanted send bounds but found they could refactor their code to avoid the problem
* Niko's MVP post: https://hackmd.io/lBfOVZjlSji7WrnB1_4Kdw
* async fn, impl trait
* `T: Foo<bar(): Send>`
* `<T as Foo>::bar(): Send`
* want to stabilize this `T: Iterator<Item: Send>`
* mgx is prototyping ([notes](https://hackmd.io/JYaDQyqMSaOrkjeA9ySAmQ))
* Final roadmap
* What do we need for the next deep dive?
* hackmd feedback
*
* Why not maybe async
* Typing `async` too much
* Edition-able / lint-able
* `async` -> `?async` not that common? <--
* trait aliases and composition
* Arguments in favor
* Semver (migrating to `?async`)
* Consistency
* Give people the option to use `async`?
* Can we try to convert a codebase using `async_trait`?
* Semver workaround
* I have `trait WidgetFactory` with async methods
* now I have `trait WidgetFactory = async MaybeAsyncWidgetFactory`
## 2023-03-03
Meeting with Sean about hyper
* Really hard to call an `async fn` from a manual future
* Captures inputs
```rust
trait Timer {
async fn sleep(&self, time: Duration);
}
timer.sleep(1).await;
//in hyper, or in a handwritten future
struct MyFuture {
timer: MyTimer,
the_current_awaitee: MyTimer::sleep<'???>,
}
impl Future for MyFuture {
fn poll(&mut self, cx: ...) {
let x = self.timer.sleep(1);
self.the_current_awaitee = x; // error
}
}
```
```rust
trait Timer {
async fn sleep(time: Duration);
}
trait Timer {
fn sleep(&self, time: Duration) -> impl Future<Output = ()>;
}
```
## 2023-02-24
Plan
* Next meeting: Talk about async closures and async iterator as an example
* Sync up with Lucio and Sean about tower (lifetime capture) question
* (Tyler) Talk with Sean about hyper portability
* Ask Eric and Yosh to write and RFC and solve the `-> impl Future` thing
* Brainstorm different syntaxes?
* Tyler should sync up with Jack Wrenn on the backtrace approach
How are we going to publish this vision
* Can mention unknowns like lifetime capture
* Be less specific about which traits are getting stabilized? "Stabilize portability traits"
* Missing H1/H2 2023 for write RFC for those traits (after the summer, doing research before the summer). Stabilize early next year.
## 2023-02-21
```rust
trait Service {
#[no_capture(self)]
async fn process(&mut self, req: &Request) -> Response {
...
}
// desugars to:
async fn process<'b>(&mut self, req: &Request) -> impl Future<Output = Response> + 'b {
async move { let req = req; ... }
}
}
fn spawn_service<T: async(Send) Service>() {
}
```
```rust
trait {
fn process<'b>(&mut self, req: &Request) -> impl 'b + Future -> Response {
async move { ... }
}
}
```
## 2023-02-13
### async closures
problems you hit today:
```rust
fn filter<F, G>(f: F)
where
F: FnMut(&Self::Item) -> G,
G: Future<Output = bool>
// G cannot capture the argument to `F`
```
what moro does is use a macro for this:
```rust
fn filter<F, G>(f: F)
where
F: FnMut(&Self::Item) -> Box<dyn Future<Output = bool> + '_>,
// G cannot capture the argument to `F`
```
cramertj said something about this in the async interview from way back when, something about mutable context?? also: the closure cannot capture the closure `F` (temporarily). Didn't hit moro because it's an FnOnce closure.
## 2023-02-06
* `Read`
* `async Read`
* this is a distinct trait
* `async(Send) Read`
* this is like "async Read" plus where-clauses
* `async Read * Send`
* the `* Send` mechanism could be "all RPITITs" or somethinglike:
* "all associated types that are designated as 'inheriting' Send become Send"
* and it so happens that the return types of async fn are (by default, at least) so designated
* perhaps other RPITIT are as well?
today you write
* `T: Foo + Send`
but I kind of what you to write
* `T: send Foo` except not that :)
* `T: Send(Foo)`
* `T: async Foo[Send]`
* `T: Send Foo`
* `T: Send async Foo`
* `T: async Send Foo`
* `T: Foo<Send>`
* `T: Log<log(): Send> + Send + 'static`
vision doc draft https://hackmd.io/UOoD7w7MQ6CvoC66DdX_Rg?edit
## 2023-01-27
Tower?
* Not capturing all input lifetimes
wg leadership, initiative
Ongoing stuff
* Stabilizing static AFIT
* Send bounds design (RTN)
* Dyn AFIT design & impl
* Big vision
Conclusions and next steps
* Empower Yosh
* Niko to author an outline of Async 2024 Vision Doc for next Friday
* Blog post about .. something?
* Tyler to finish doc on post-monomorphization errors, show lang team
```rust
fn foo<T>(x: T) {
where T: Debug {
dbg!(&x);
}
}
```
## 2023-01-23
* wg-async leadership
* Language user studies
### Niko's roadmap
* Rust 2024
* Async functions in traits, static + dynamic dispatch
* Async closures (stretch goal)
* Read/write trait (stretch goal; interacts with keyword generics)
* Question marks
* Futures concurrency library
* Revisit the async vision doc with experience from
* Keyword generics exploration
* Structured concurrency exploration
* Async drop -- linearity exploration
* Other portability traits exploration
* Big pieces
* keyword generics -- some way to write traits that can be "made" async (or sync)
* runtime interface -- some way to write `async fn main` or libraries that are independent from runtime, and which have access to comparable facilities like those available in std now
* e.g., new task etc
*
```rust
// V1
trait AsyncRead { async fn read(&mut self, ...); }
// V2
trait AsyncRead = async std::io::Read;
```
### High-level goals (to publish)
You should be able to write code that
* is runtime-independent or specialized to a particular runtime
* is both sync and async
* awaits inside of iterator combinators like `map`
```rust
async fn main() {
while let Some(x) = std::io::listener("localhost:").next().await {
}
}
```
### Decision
* Rust 2024
* Async functions in traits, static + dynamic dispatch
* Async closures (stretch goal)
* Read/write trait (stretch goal; interacts with keyword generics)
* Rust 2027
* We need libraries that ensure
* cancel safety --
* when cancellation occurs, will perform required cleanup
* structured concurrency
* you can easily express concurrent patterns
* you can easily cancel a piece of work and all its constituents
* You should be able to write code that
* is runtime-independent or specialized to a particular runtime
* is both sync and async
* awaits inside of iterator combinators like `map`
* Runtime independent
* https://rustacean-principles.netlify.app/how_rust_empowers.html
* Working backwards, questions we need to answer
* Keyword generics, viable path?
* Some kind of platform trait
### Outline
* How async Rust should feel, properties it should have
* Reliable: if it compiles it works, just like with sync Rust, no surprise gotchas at runtime
*
* Performant:
* Supportive:
* Productive:
* you can easily express concurrent patterns
* Versatile: Able to support specialized runtimes, ranging from no-std to "your OS here" to blah blah
* Gaps people still experience
* Reliable:
* https://rust-lang.github.io/wg-async/vision/submitted_stories/status_quo/barbara_battles_buffered_streams.html
* cancel safety
* Runtime mismatches
* Async drop
* Supportive
* lack of good dev tooling
* Productive
* async fn in traits doesn't work
* Performant
* io-uring, uh oh
* Versatile:
*
* What we see as the most promising candidates
* Keyword generics
* Structured concurrency
* Roadmap, current activities
* Our approach
* Prioritize helping current users, practical progress
* Figuring out what we need to learn to design next iteration
* Async function in trait, static dispatch
* Send bounds
* Async function in trait, dynamic dispatch
* Futures-concurrency: experimental trait, offers
* Keyword generics
## 2023-01-09
* Stabilizing async fn in traits
* case studies
* tower trait <-- get lucio franco and seanmcarthur to do this maybe ?
* ???
* implementation issues resolved
* compiler-errors says this has to do with something cjgillot is working on
* Send bounds:
* show in the case studies how it will be
* make the case for the design
* write a blog post to get people acquainted with the idea
* implementation prototype <-- compiler-errors did some stuff here
* write an RFC
* finalize implementation
* desired outcome
* identify blockers to tower adopting AFIT
* tmandry to start a thread about scheduling a deep dive
* async in 2023!! what can we do!!!!!
* async fns in traits stabilized
* dyn nightly
* niko + yosh talking about keyword generics
* https://hackmd.io/kDl6iN5tSCKSu0OREe7Hhw
# 2021-2022
* [Async Fundamentals Notes: 2022](/F1imiSfDSZON6OaO-E5s7Q)
* [Async fundamentals ere long ago (2021)](/XHz8QAO2RMGJ_kSOC6XJ0g)