Async Traits
Async methods in traits 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 specific pitfalls around async fn:
-
Return-position
impl Traitcaptures all in-scope lifetimes (so certain patterns of borrowing cannot be expressed). -
Async traits cannot be used with trait objects (
dyn Traitsupport).
The async_trait crate provides a workaround for dyn support through a macro, with specific caveats:
// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use std::time::Instant;
use tokio::time::{Duration, sleep};
#[async_trait]
trait Sleeper {
async fn sleep(&self);
}
struct FixedSleeper {
sleep_ms: u64,
}
#[async_trait]
impl Sleeper for FixedSleeper {
async fn sleep(&self) {
sleep(Duration::from_millis(self.sleep_ms)).await;
}
}
async fn run_all_sleepers_multiple_times(
sleepers: Vec<Box<dyn Sleeper>>,
n_times: usize,
) {
for _ in 0..n_times {
println!("Running all sleepers...");
for sleeper in &sleepers {
let start = Instant::now();
sleeper.sleep().await;
println!("Slept for {} ms", start.elapsed().as_millis());
}
}
}
#[tokio::main]
async fn main() {
let sleepers: Vec<Box<dyn Sleeper>> = vec![
Box::new(FixedSleeper { sleep_ms: 50 }),
Box::new(FixedSleeper { sleep_ms: 100 }),
];
run_all_sleepers_multiple_times(sleepers, 5).await;
}
-
async_traitis 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 traitare 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:- RPIT: short for return-position
impl Trait. - RPITIT: short for return-position
impl Traitin trait (RPIT in trait).
- RPIT: short for return-position
-
Try creating a new sleeper struct that will sleep for a random amount of time and adding it to the
Vec.