Асинхронні трейти
Асинхронні методи у трейтах було стабілізовано нещодавно, у випуску 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; }
-
async_trait
простий у використанні, але зауважте, що для цього він використовує виділення в купі. Цей розподіл купи має накладні витрати на продуктивність. -
Проблеми з мовною підтримкою для
async trait
глибокі в Rust і, мабуть, не варті детального опису. Ніко Мацакіс добре пояснив їх у цій публікації, якщо вам цікаво копати глибше. -
Спробуйте створити нову сплячу структуру, яка буде спати протягом випадкового періоду часу, і додайте її до Vec.