Асинхронні трейти

Асинхронні методи у трейтах було стабілізовано нещодавно, у випуску 1.75. Це вимагало підтримки використання impl Trait з позицією повернення (RPIT) у трейтах, оскільки десигнування для async fn включає -> impl Future<Output = ...>.

Однак, навіть з нативною підтримкою сьогодні існують деякі підводні камені навколо async fn та RPIT у трейтах:

  • Позиція повернення impl Trait фіксує всі терміни життя в межах області застосування (тому деякі моделі запозичення не можуть бути виражені)

  • Трейти, методи яких використовують позицію повернення impl trait або async, не сумісні з dyn.

Якщо нам потрібна підтримка dyn, то крейт async_trait надає обхідний шлях за допомогою макросу, з деякими застереженнями:

use async_trait::async_trait;
use std::time::Instant;
use tokio::time::{sleep, Duration};

#[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!("запуск всіх сплячих..");
        for sleeper in &sleepers {
            let start = Instant::now();
            sleeper.sleep().await;
            println!("проспав {}мс", 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;
}
This slide should take about 5 minutes.
  • async_trait простий у використанні, але зауважте, що для цього він використовує виділення в купі. Цей розподіл купи має накладні витрати на продуктивність.

  • Проблеми з мовною підтримкою для async trait глибокі в Rust і, мабуть, не варті детального опису. Ніко Мацакіс добре пояснив їх у цій публікації, якщо вам цікаво копати глибше.

  • Спробуйте створити нову сплячу структуру, яка буде спати протягом випадкового періоду часу, і додайте її до Vec.