Async traits

RecipeCratesCategories
Async traitscat-asynchronous

As of Rust 1.75, it is possible to have async⮳ functions in traits:

struct MyHealthChecker;

trait HealthCheck {
    async fn check(&mut self) -> bool; // <- async fn defined in a Trait
}

impl HealthCheck for MyHealthChecker {
    async fn check(&mut self) -> bool {
        // async fn implementation in the associated impl block
        do_async_op().await
    }
}

async fn do_health_check(mut hc: impl HealthCheck) {
    if !hc.check().await {
        // use as normal
        log_health_check_failure().await;
    }
}

async fn do_async_op() -> bool {
    true
}

async fn log_health_check_failure() {}

#[tokio::main]
async fn main() {
    let hc = MyHealthChecker;
    do_health_check(hc).await;
}

Stabilizing async fn in traits in 2023

This is in turn enabled by return-position impl Trait in traits, since async fn is sugar for functions that return -> impl Future.

// TODO
#![allow(dead_code)]

trait Container {
    fn items(&self) -> impl Iterator<Item = u8>; // <-- return Impl in a trait
}

struct MyContainer {
    items: Vec<u8>,
}

impl Container for MyContainer {
    fn items(&self) -> impl Iterator<Item = u8> {
        self.items.iter().cloned()
    }
}

fn main() {
    let c = MyContainer {
        items: vec![1, 2, 3],
    };
    for i in c.items {
        println!("{}", i);
    }
}

Note that there are still caveats for public traits - see Announcing async fn and return-position impl Trait in traits⮳.

In addition, traits that use -> impl Trait and async fn are not object-safe, which means they lack support for dynamic dispatch. In the meanwhile, use the async-trait crate.

async-trait async_trait-github cat-asynchronous

use async_trait::async_trait;

#[async_trait]
trait Advertisement {
    async fn run(&self);
}

struct Modal;

#[async_trait]
impl Advertisement for Modal {
    async fn run(&self) {
        self.render_fullscreen().await;
        for _ in 0..4u16 {
            remind_user_to_join_mailing_list().await;
        }
        self.hide_for_now().await;
    }
}

impl Modal {
    async fn render_fullscreen(&self) {}

    async fn hide_for_now(&self) {}
}

async fn remind_user_to_join_mailing_list() {}

#[tokio::main]
async fn main() {
    Modal.run().await;
}