异步特征

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

However, even with the native support today there are some pitfalls around async fn and RPIT in traits:

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

  • Traits whose methods use return-position impl trait or async are not dyn compatible.

If we do need dyn support, the crate async_trait provides a workaround 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. }
  • async_trait 易于使用,但请注意,它通过堆分配来实现这一点。这种堆分配会产生性能开销。

  • 对于 async trait 的语言支持中的挑战是深入 Rust的,并且可能不值得深入描述。如果您对深入了解感兴趣,Niko Matsakis 在这篇文章中对它们做了很好的解释。

  • 尝试创建一个新的 sleeper 结构,使其随机休眠一段时间,并将其添加到 Vec 中。