Dynamic dispatch on async fn

I wrote some thoughts about this topic. Would appreciate any correctness or your ideas!

What's wanted?

pub trait AsyncRead {
    async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
}

async fn call(file: &mut dyn AsyncRead /* 👈 */) -> io::Result<usize> {
    file.read(&mut vec![]).await
}
  1. call async fn in a trait object
  2. no heap allocation on return value, i.e. no -> Pin<Box<dyn ...>>

Send and Sync problems are not considered for now.

Summary

The code is in examples folder:

# file name is directly dispatchable no head allocated return value extra description
1 returns-box-trait-object.rs :white_check_mark: :x: widely used; simple
2 returns-stack-future.rs :white_check_mark: :white_check_mark: simple; fixed allocation size but heap allocation as a fallback
3 returns-future-in-trait-with-supertrait.rs :x: :white_check_mark: used in async-std; the pattern is an inspiration
4 afit-with-supertrait.rs :x: :white_check_mark: takes the inspiration above with AFIT
5 dynosaur.rs :x: :x: promising idea and APIs to support referenced erased types
2 Likes

There's another solution: store the future inside self and return &mut dyn Future from the function. This way there's no allocation, and no size limit. But it only works with &mut self and not with &self because only &mut self enforces at most one caller at the time.

1 Like