Search code examples
rusttraitsrust-tokio

Is it possible to use async_trait with dynamic dispatch


I am trying to create a async_trait that use a dyn reference to Iterator.

Here is an example of my code :

#[async_trait::async_trait]
trait StringPrinter { 
    async fn print(a: &mut dyn Iterator<Item = String>); 
}

struct MyStringPrinter{} 

#[async_trait::async_trait]
impl StringPrinter for MyStringPrinter { 
    async fn print(a: &mut dyn Iterator<Item = String>){
       a.for_each(|x| println!("{}", x));     
       tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; 
    }
}

But the compiler complains on : future created by async block is not 'Send'

On the other hand - simple version of async function works well:

async fn print_iterator(a: &mut dyn Iterator<Item = String>) { 
    a.for_each(|x| println!("{}", x));     
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; 
}

Any idea what is different ? or any idea for workaround?


Solution

  • Yes, it is possible, after all the #[async_trait] proc-macro rewrites the method to use a boxed future. This is explained in the documentation of the crate:

    Async fns get transformed into methods that return Pin<Box<dyn Future + Send + 'async>> and delegate to a private async freestanding function.

    But generated future will be Send if and only if all its members implement Send. Yet your iterator does not:

    &mut dyn Iterator<Item = String>
    

    Just change that to:

    &mut (dyn Iterator<Item = String> + Send)