Async Traits

RecipeCratesCategories
Async Traitsasync-traitcat-asynchronous

std cat-asynchronous

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

struct MyHealthChecker;

/// A trait for checking the health of a system.
trait HealthCheck {
    /// Checks the health of the system.
    ///
    /// Returns `true` if the system is healthy, `false` otherwise.
    async fn check(&mut self) -> bool;
}

impl HealthCheck for MyHealthChecker {
    // `async fn` implementation in the associated `impl` block.
    async fn check(&mut self) -> bool {
        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;
    } else {
        println!("Health check was normal");
    }
}

async fn do_async_op() -> bool {
    true
}

async fn log_health_check_failure() {
    println!("Health check failure");
}

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

[Stabilizing async fn in traits in 2023]blog-stabilizing-async-fn-in-traits⮳.

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

/// This example demonstrates how to use `impl Trait` in a trait definition.
///
/// The `Container` trait defines a method `items` that returns
/// an iterator over `u8` values. The specific type of the iterator is not
/// specified in the trait definition, but is instead left to the implementor
/// to decide.
#[allow(dead_code)]
trait Container {
    /// Returns an iterator over the items in the container.
    fn items(&self) -> impl Iterator<Item = u8>;
}

/// A concrete implementation of the `Container` trait.
struct MyContainer {
    items: Vec<u8>,
}

/// Implementation of the `Container` trait for `MyContainer`.
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][p-async] fn and return-position impl Trait in traits]blog-announcing-async-fn⮳.

In addition, traits that use -> impl Trait and [async][p-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;

// Define an asynchronous trait named `Advertisement`.
#[async_trait]
trait Advertisement {
    async fn run(&self);
}

struct Modal;

// Implement the `Advertisement` trait for the `Modal` struct.
#[async_trait]
impl Advertisement for Modal {
    async fn run(&self) {
        // Call the `render_fullscreen` method on `self` and await its
        // completion.
        self.render_fullscreen().await;
        // Loop 4 times.
        for _ in 0..4u16 {
            // Call the `remind_user_to_join_mailing_list` function and await
            // its completion.
            remind_user_to_join_mailing_list().await;
        }
        // Call the `hide_for_now` method on `self` and await its completion.
        self.hide_for_now().await;
    }
}

// Implement methods for the `Modal` struct.
impl Modal {
    async fn render_fullscreen(&self) {
        println!("Render fullscreen");
    }

    async fn hide_for_now(&self) {
        println!("Hide for now");
    }
}

async fn remind_user_to_join_mailing_list() {
    println!("Please join our mailing list");
}

#[tokio::main]
async fn main() {
    // Create an instance of `Modal` and call the `run` method, awaiting its
    // completion.
    Modal.run().await;
}

Related Topics

  • Trait Objects.
  • Traits.