# Blog series: Dyn async in traits (continues)

**URL:** https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403
**Category:** language design
**Created:** [September 18, 2022, 5:55pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403 "2022-09-18T17:55:43Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![nikomatsakis](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nikomatsakis/32/5410_2.png) [@nikomatsakis](https://internals.rust-lang.org/u/nikomatsakis)
#### Post date: [September 18, 2022, 5:55pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/1 "2022-09-18T17:55:43Z")

</div>

Continuing the discussion from [Blog series: Dyn async in traits](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits/15449) -- I've posted some more blog posts in this series:

- [Part 6](https://smallcultfollowing.com/babysteps/blog/2021/10/15/dyn-async-traits-part-6/) -- Oct 15, 2021
- [Part 7: A design emerges?](https://smallcultfollowing.com/babysteps/blog/2022/01/07/dyn-async-traits-part-7/) -- Jan 7, 2021
- [Part 8: the soul of Rust?](https://smallcultfollowing.com/babysteps/blog/2022/09/18/dyn-async-traits-part-8-the-soul-of-rust/) -- Sep 18, 2022
- [What I meant by the soul of Rust](https://smallcultfollowing.com/babysteps/blog/2022/09/19/what-i-meant-by-the-soul-of-rust/)
- [Part 9: call-site selection](https://smallcultfollowing.com/babysteps/blog/2022/09/21/dyn-async-traits-part-9-callee-site-selection/)

As always, I'd love to hear what people think.

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [September 18, 2022, 8:18pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/2 "2022-09-18T20:18:52Z")

</div>

What would be the tradeoffs of using the [placement by return](https://github.com/rust-lang/rfcs/pull/2884) RFC for this? Potential advantages:

- If Rust alloca ever happens, returning unsized futures can "just work" in non-async contexts.
- In contexts where alloca doesn't work, boxing the future is "just" `Box::new_with(|| ..)`, no need for dedicated adapter types.
  - This also means that, if the storages proposal happens, all the different storage types will automatically be available to store those unsized futures. `Box::new_with(|| ..)` will support them all, a one-stop shop error messages can point to

- You can choose allocation strategy at the point you call the async method. This is more flexible (but potentially less ergonomic) than choosing allocation strategy for all callers when you create the `dyn` adapter.
  - This also means you can choose a different allocation strategy for different methods on the same `dyn AsyncTrait`. For example, one async trait method might return a future that is probably small and fits on the stack, while another might be more likely to return a large future that belongs on the heap.

---

<div class="post-metadata">

### Author: ![y86-dev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/y86-dev/32/9867_2.png) [@y86-dev](https://internals.rust-lang.org/u/y86-dev)
#### Post date: [September 18, 2022, 8:30pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/3 "2022-09-18T20:30:44Z")

</div>

### Caller site `Box` caching

I am not sure about caller site box caching, can it handle the first function?

```rust
async fn print_interleave<'a, T: Debug>(
    mut iter: &'a mut dyn AsyncIterator<Item = T>,
    mut other: &'a mut dyn AsyncIterator<Item = T>,
) {
    while let Some(next) = iter.next().await {
        core::mem::swap(&mut iter, &mut other);
        println!("{next:?}");
    }
    while let Some(next) = other.next().await {
        println!("{next:?}");
    }
}

async fn print_interleave2<T: Debug>(
    iters: &mut VecDeque<&mut dyn AsyncIterator<Item = T>>,
) {
    while let Some(mut iter) = iters.pop_front() {
        while let Some(next) = iter.next().await {
            iters.push_back(iter);
            println!("{next:?}");
            iter = iters
                .pop_front()
                .expect("we just push_back'ed an iter, there has to be one!");
        }
    }
}

```

I am pretty sure that no solution will be able to caller-site cache the second function.

### `Box` it as a default

> [@](#):
>
> As an example of where this might matter, it might be that you are writing some sensitive systems code where allocation is something you always do with great care. It doesn’t mean the code is no-std, it may have access to an allocator, but you still would like to know exactly where you will be doing allocations. Today, you can audit the code by hand, scanning for “obvious” allocation points like `Box::new` or `vec![]`. Under this proposal, while it would still be _possible_, the presence of an allocation in the code is much less obvious. The allocation is “injected” as part of the vtable construction process. To figure out that this will happen, you have to know Rust’s rules quite well, and you also have to know the signature of the callee (because in this case, the vtable is built as part of an implicit coercion). In short, scanning for allocation went from being relatively obvious to requiring a PhD in Rustology. Hmm.

I would like to give the [rust for linux project](https://github.com/Rust-for-Linux/linux) as an example. Kernel developers would _absolutely_ not like implicit allocations. `async` is already being used in some experimental drivers.

### General thoughts

I think it would be better to select the type of returning at the call site. So the ABI for dynamic dispatch `async` functions should include a strategy selection stub/multiple functions should be generated with the different strategies.

Most often the caller will have a better idea of the constraints than the callee. It will also prevent the following scenario: What if a dependency suddenly changed to use `Boxing` instead of `InlineAsyncIterator`? Implementation details should not leak into my crate!

I would _really_ like to see an attempt at a solution with the placement by return RFC that @Jules-Bertholet alreay mentioned.

---

<div class="post-metadata">

### Author: ![kornel](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kornel/32/2711_2.png) [@kornel](https://internals.rust-lang.org/u/kornel)
#### Post date: [September 18, 2022, 9:06pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/4 "2022-09-18T21:06:19Z")

</div>

I do value the transparency and control aspects. I'd be fine with an explicit `Boxing::new()` adapter.

---

<div class="post-metadata">

### Author: ![PoignardAzur](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/poignardazur/32/6464_2.png) [@PoignardAzur](https://internals.rust-lang.org/u/PoignardAzur)
#### Post date: [September 18, 2022, 10:25pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/5 "2022-09-18T22:25:59Z")

</div>

> [@Jules-Bertholet](#):
>
> What would be the tradeoffs of using the [placement by return](https://github.com/rust-lang/rfcs/pull/2884) RFC for this?

It feels like every single time Niko writes one of those posts, someone will mention placement return, and every single time Niko ignores it.

We even had a pretty big discussion on the subject on zulip a few months back, where various trade-offs were mentioned.

It's pretty disappointing none of that was mentioned in Niko's post. At that point it feels like arguing in circles.

---

<div class="post-metadata">

### Author: ![withoutboats](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/withoutboats/32/4560_2.png) [@withoutboats](https://internals.rust-lang.org/u/withoutboats)
#### Post date: [September 18, 2022, 10:29pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/6 "2022-09-18T22:29:34Z")

</div>

(NOT A CONTRIBUTION)

In my opinion, the boxing adapter will just be yet another annoying, frustrating, and undiscoverable thing that users in the async ecosystem will have to deal with. This will continue to harm async Rust's reputation unnecessarily.

Pragmatically, the anathema on allocation has never been sensible in my opinion. Rust will happily let you memcpy megabytes with no transparency, but heaven forbid you increment an Rc's refcount silently. These decisions were arbitrary and don't represent a philosophy that actually benefits users, from my perspective. As async is actually used in production, any time you're awaiting you're probably performing network IO of some kind and the allocation is literally orders of magnitude cheaper.

However, I think the fact that this hasn't been decided, nearly 3 years after shipping the MVP, does a lot more harm to async Rust's reputation than requiring some boxing adapter will do. The Rust project (and especially the language team) have an attitude toward discussions and the consensus process that in my opinion is toxic and doing a lot of harm to Rust the product (and I include my past conduct on await syntax in this assessment). I will be happy to see any solution to async trait methods shipped in stable Rust.

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [September 18, 2022, 10:44pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/7 "2022-09-18T22:44:52Z")

</div>

> [@withoutboats](#):
>
> Rust will happily let you memcpy megabytes with no transparency

The placement by return RFC would make such dangerous memcpys less common and easier to avoid, in addition to allowing total control over heap allocation and unsized returns.

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [September 18, 2022, 11:16pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/8 "2022-09-18T23:16:53Z")

</div>

To elaborate, here is an example use case that is **not `no_std` specific** (can happen in regular application code) and that only placement-by-return can address:

```rust
trait AsyncFoo {
    async fn do_lots_of_work_with_a_big_future(&self);

    async fn very_simple_function_small_future(&self);
}

async fn do_the_work(foo: &dyn AsyncFoo) {
   // We want to box this future because it's really big.
   // We don't care if this is slow
   Box::new_with(|| foo.do_lots_of_work_with_a_big_future()).await;

   // Tight loop! Business critical!
   // Every millisecond counts!
   loop {
       // Heap allocation would be too slow here
       StackBox::new_with(|| foo.very_simple_function_small_future()).await;
   }
}

```

---

<div class="post-metadata">

### Author: ![Skepfyr](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skepfyr/32/9826_2.png) [@Skepfyr](https://internals.rust-lang.org/u/Skepfyr)
#### Post date: [September 18, 2022, 11:42pm UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/9 "2022-09-18T23:42:49Z")

</div>

Edit: Reading the placement-by-return RFC I think this is the same suggestion as the above comments.

The way I would naturally expect this to work is like this:

```rust
async fn use_dyn(iter: &dyn AsyncIterator) {
    Box::new(iter.next()).await
}

```

Essentially the alloca method but just require boxing (or some inderection) at await points. I think that's the placement new method others above have mentioned.

Specifically I think it requires:

- The AsyncIterator trait object to contain the vtable for the returned future type, so that the caller knows its size, alignment, and poll method.
- Something like unsized locals and placement new.

This allows the ability to do most (all?) of the patterns described, and to me is simple, transparent, easy to make perrormant, easy to produce understandable errors, and very similar to Boxing in terms of productivity. It also has the benefit of not using any particularly weird features, I'm worried a bit that Boxing would be quite magic and people would want to write similar but subtly different versions but not be able to.

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [September 19, 2022, 12:45am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/10 "2022-09-19T00:45:58Z")

</div>

> [@Skepfyr](#):
>
> `Box::new(iter.next()).await`

Placement by return is basically this, with one complication. Function arguments are passed on the stack before a function is called, but you can't put unsized values on an `async fn`'s stack. So instead, you pass in a closure to `Box::new_with`. `new_with` calls the closure and provides a _place_ on the heap for the closure to _return_ the unsized value (hence "placement by return"). The result looks like:

```rust
Box::new_with(|| iter.next()).await

```

---

<div class="post-metadata">

### Author: ![JoJoJet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jojojet/32/9655_2.png) [@JoJoJet](https://internals.rust-lang.org/u/JoJoJet)
#### Post date: [September 19, 2022, 1:52am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/11 "2022-09-19T01:52:56Z")

</div>

> [@Jules-Bertholet](#):
>
> ```rust
> Box::new_with(|| iter.next()).await
> 
> ```

I feel this could be made more elegant by having `Box::new` lazily evaluate its argument. Something like

```rust
impl<T: ?Sized> Box<T> {
    pub fn new(lazy val: T) -> Self { ... }
}

```

Then it really could just be

```rust
Box::new(iter.next()).await

```

---

<div class="post-metadata">

### Author: ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)
#### Post date: [September 19, 2022, 2:03am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/12 "2022-09-19T02:03:50Z")

</div>

> [@Jules-Bertholet](#):
>
> you can't put unsized values on an `async fn`'s stack

Actually, I suppose you could, they just can't be held across an `await`. Hmmm...

---

<div class="post-metadata">

### Author: ![y86-dev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/y86-dev/32/9867_2.png) [@y86-dev](https://internals.rust-lang.org/u/y86-dev)
#### Post date: [September 19, 2022, 9:38am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/13 "2022-09-19T09:38:39Z")

</div>

> [@PoignardAzur](#):
>
> We even had a pretty big discussion on the subject on zulip a few months back, where various trade-offs were mentioned.

I have only been able to find [this](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/return.20position.20impl.20Trait.20in.20dyn.20Trait/near/276384740), was that the whole discussion?

I would like to understand why placement by return cannot be used here.

> What follows is, I think, an exhaustive list of the various ways one might handle the situation.

I think it should be listed as an option.

---

<div class="post-metadata">

### Author: ![PoignardAzur](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/poignardazur/32/6464_2.png) [@PoignardAzur](https://internals.rust-lang.org/u/PoignardAzur)
#### Post date: [September 19, 2022, 9:41am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/14 "2022-09-19T09:41:53Z")

</div>

> [@y86-dev](#):
>
> I have only been able to find [this](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/return.20position.20impl.20Trait.20in.20dyn.20Trait/near/276384740), was that the whole discussion?

Yeah, it was.

(I was going to go dig for it, thanks for saving me the time)

---

<div class="post-metadata">

### Author: ![Skepfyr](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skepfyr/32/9826_2.png) [@Skepfyr](https://internals.rust-lang.org/u/Skepfyr)
#### Post date: [September 19, 2022, 9:59am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/15 "2022-09-19T09:59:20Z")

</div>

Turns out this kinda already works (after enabling quite a lot of features), the only thing that's really missing is for dyn async traits to return `dyn Future`s. It would be quite cool for trait objects to return trait objects instead of associated types where possible, making a bunch of traits object safe.

> **[Rust Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=4c47a3fc34e5dbe90af2a69b33b9c1ef)**
>
> A browser interface to the Rust compiler to experiment with the language

---

<div class="post-metadata">

### Author: ![y86-dev](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/y86-dev/32/9867_2.png) [@y86-dev](https://internals.rust-lang.org/u/y86-dev)
#### Post date: [September 19, 2022, 10:34am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/16 "2022-09-19T10:34:59Z")

</div>

So I found [this](https://hackmd.io/@nikomatsakis/S1xxjkZGc#Unsized-returns-aka-caller-decides) document created in the zulip discussion posted above. It states:

> async contexts, and generators in general, do not support unsized allocation on the stack (also known as alloca). This is because their stack values that exist across await points are pre-allocated. Futures being awaited always exist across await points, so this approach would not support stack allocation.

As @Jules-Bertholet already pointed out:

> [@Jules-Bertholet](#):
>
> Actually, I suppose you could, they just can't be held across an `await`. Hmmm...

This is because the generator has two stacks, one normal function stack and one generator stack that is preserved across `yield`/`await`. The function stack is renewed every time. I think that the limitation then would just be "before the next `yield`/`await`, figure out where to store this". There would need to be some support for storing `dyn Trait` in fixed-size fields, example:

```rust
async fn print_all(iter: &mut dyn AsyncIterator<Item = String>) {
    while let Some(next: dyn Future<Output = String>) = iter.next() {
        if let next: { dyn Future<Output = String>; 24 } = next {
            println!("{}", next.await);
        } else {
            let string = Box::pin(next).await;
            println!("{string}");
        }
    }
}

```

Here `{ dyn Trait; $size }` stands for a size capped `dyn` trait object. Using pattern matching one can assign `dyn Trait` to `{ dyn Trait; $size }`. In code that does not care about this level of control, one can still use the `Boxing` wrapper:

```rust
async fn print_all(iter: &mut dyn AsyncIterator<Item = String>) {
    let iter = Boxing::new(iter);
    while let Some(next: Pin<Box<dyn Future<Output = String>>>) = iter.next() {
        println!("{}", next.await);
    }
}

```

---

<div class="post-metadata">

### Author: ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)
#### Post date: [September 19, 2022, 10:47am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/17 "2022-09-19T10:47:17Z")

</div>

We already have multiple implementations of size-capped `dyn` trait objects, e.g. [`stack_dst::Value`](https://docs.rs/stack_dst/latest/stack_dst/type.Value.html) for just a fixed size allocation or [`smallbox`](https://docs.rs/smallbox/latest/smallbox/) for a version that automatically promotes to the heap when the size is exceeded. It seems plausible for these to support `unsized-fn-params` as a way to pass a bare `dyn` value in. (Or pretty trivial to write a `SmallBoxing::<S16>` adaptor similar to `Boxing` (if we look at a TAIT +GAT based approach rather than a `dyn*` one)).

---

<div class="post-metadata">

### Author: ![PoignardAzur](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/poignardazur/32/6464_2.png) [@PoignardAzur](https://internals.rust-lang.org/u/PoignardAzur)
#### Post date: [September 19, 2022, 10:47am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/18 "2022-09-19T10:47:25Z")

</div>

> [@y86-dev](#):
>
> I have only been able to find [this](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/return.20position.20impl.20Trait.20in.20dyn.20Trait/near/276384740), was that the whole discussion?

Wait, no, I continued the discussion in [another thread](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/RPIT.20in.20dyn.20Traits.20-.20Unsized.20returns).

---

<div class="post-metadata">

### Author: ![programmerjake](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programmerjake/32/5893_2.png) [@programmerjake](https://internals.rust-lang.org/u/programmerjake)
#### Post date: [September 19, 2022, 10:52am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/19 "2022-09-19T10:52:19Z")

</div>

> [@nikomatsakis](#):
>
> [Part 7: A design emerges?]9https://smallcultfollowing.com/babysteps/blog/2022/01/07/dyn-async-traits-part-7/) -- Jan 7, 2021

you ended up with a 9 instead of (

---

<div class="post-metadata">

### Author: ![programmerjake](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programmerjake/32/5893_2.png) [@programmerjake](https://internals.rust-lang.org/u/programmerjake)
#### Post date: [September 19, 2022, 11:02am UTC](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403/20 "2022-09-19T11:02:45Z")

</div>

also the svg diagram is busted: [Baby Steps](https://smallcultfollowing.com/babysteps/blog/2022/01/07/dyn-async-traits-part-7/#the-design-from-22222-feet)

[Next page](https://internals.rust-lang.org/t/blog-series-dyn-async-in-traits-continues/17403.md?page=2)
