Async Traits

Async methods in traits are were stabilized in the 1.75 release. This required support for using return-position impl Trait in traits, as the desugaring for async fn includes -> impl Future<Output = ...>.

However, even with the native support, there are some pitfalls around async fn:

  • Return-position impl Trait captures all in-scope lifetimes (so some patterns of borrowing cannot be expressed).

  • Async traits cannot be used with trait objects (dyn Trait support).

The async_trait crate provides a workaround for dyn support through a macro, with some caveats:

  1. use async_trait::async_trait;
  2. use std::time::Instant;
  3. use tokio::time::{sleep, Duration};
  4. #[async_trait]
  5. trait Sleeper {
  6.     async fn sleep(&self);
  7. }
  8. struct FixedSleeper {
  9.     sleep_ms: u64,
  10. }
  11. #[async_trait]
  12. impl Sleeper for FixedSleeper {
  13.     async fn sleep(&self) {
  14.         sleep(Duration::from_millis(self.sleep_ms)).await;
  15.     }
  16. }
  17. async fn run_all_sleepers_multiple_times(
  18.     sleepers: Vec<Box<dyn Sleeper>>,
  19.     n_times: usize,
  20. ) {
  21.     for _ in 0..n_times {
  22.         println!("Running all sleepers...");
  23.         for sleeper in &sleepers {
  24.             let start = Instant::now();
  25.             sleeper.sleep().await;
  26.             println!("Slept for {} ms", start.elapsed().as_millis());
  27.         }
  28.     }
  29. }
  30. #[tokio::main]
  31. async fn main() {
  32.     let sleepers: Vec<Box<dyn Sleeper>> = vec![
  33.         Box::new(FixedSleeper { sleep_ms: 50 }),
  34.         Box::new(FixedSleeper { sleep_ms: 100 }),
  35.     ];
  36.     run_all_sleepers_multiple_times(sleepers, 5).await;
  37. }

This slide should take about 5 minutes.

  • async_trait is easy to use, but note that it’s using heap allocations to achieve this. This heap allocation has performance overhead.

  • The challenges in language support for async trait are too deep to describe in-depth in this class. See this blog post by Niko Matsakis if you are interested in digging deeper. See also these keywords:

  • Try creating a new sleeper struct that will sleep for a random amount of time and adding it to the Vec.