4.3 异步 Trait

03-异步 Trait — Comprehensive Rust

译文 · 基于 Comprehensive Rust

原文链接: https://google.github.io/comprehensive-rust/concurrency/async-pitfalls/async-traits.html

4.3 异步 Trait

Trait 中的异步方法在 1.75 版本中稳定。这需要支持在 trait 中使用返回位置的 impl Trait,因为 async fn 的脱糖包含 -> impl Future<Output = ...>。

不过,即便有原生支持,围绕 async fn 仍有特定陷阱:

  • 返回位置的 impl Trait 会捕获所有作用域内的生命周期(因此某些借用模式无法表达)。

  • 异步 trait 不能与 trait 对象(dyn Trait 支持)一起使用。

async_trait crate 通过宏为 dyn 支持提供变通方法,但有特定注意事项:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 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_trait 易于使用,但注意它通过堆分配实现这一点。堆分配有性能开销。

  • 语言对 async trait 支持的挑战太深,本课无法深入描述。若有兴趣深挖,参见 Niko Matsakis 的这篇博文。也可参见这些关键词:

  • 试着创建一个休眠随机时长的新 sleeper 结构体,并把它加入 Vec。

最后修改 August 11, 2026: 更新 (70a5af133)