async-trait

repository·master·Indexed 24 days ago

https://github.com/dtolnay/async-trait

A Rust crate providing the #[async_trait] attribute macro to enable async functions in traits. It allows async trait methods to be used with dynamic dispatch (dyn Trait) by transforming them into functions that return a pinned, boxed future. Supports generic parameters, associated types, and an optional ?Send argument for non-threadsafe futures.

Tokens
1.5K
Snippets
3
Records
8
Agent score
30%

What's inside async-trait

  1. How `#[async_trait]` works internally

    master

    The #[async_trait] macro transforms async fn methods into regular functions that return a pinned, boxed, thread-safe future: Pin<Box<dyn Future + Send + 'async_trait>>.

    This transformation allows the trait to be used with dynamic dispatch (dyn Trait), as the return type is now a concrete, sized type (the Pin<Box<...>> wrapper) rather than an opaque, compiler-generated future type.

    // Example of how the macro expands an implementation:
    impl Advertisement for AutoplayingVideo {
        fn run<'async_trait>(
            &'async_trait self,
        ) -> Pin<Box<dyn std::future::Future<Output = ()> + Send + 'async_trait>>
        where
            Self: Sync + 'async_trait,
        {
            Box::pin(async move {
                /* the original method body */
            })
        }
    }
  2. Use the `#[async_trait]` macro for async functions in traits

    master

    In Rust (up to version 1.75), using async fn in traits prevents the trait from being used as a dyn Trait (dynamic dispatch). The async_trait crate provides an attribute macro that enables async fn in traits while maintaining compatibility with dyn Trait.

    To use it, apply the #[async_trait] macro to both the trait definition and the corresponding impl blocks.

    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) {
            // implementation
        }
    }
  3. Use the `#[async_trait]` macro for async trait methods

    master

    The #[async_trait] attribute macro allows you to define and implement asynchronous functions within traits while maintaining support for dynamic dispatch (dyn Trait). This is necessary because native Rust async functions in traits currently do not support being used as dyn Trait (they are not 'dyn compatible').

    To use it, apply #[async_trait] to both the trait definition and the implementation blocks containing async functions.

    Supported features include:

    • Self by value, reference, or mutable reference.
    • Any number of arguments and return values.
    • Generic and lifetime parameters.
    • Associated types.
    • Mixing async and non-async functions in the same trait.
    • Default implementations.
    • Elided lifetimes (though they must be explicitly handled as described in the 'Elided lifetimes' section).
    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) {
            // ... implementation
        }
    }
  4. Handle elided lifetimes in async trait methods

    master

    The async fn syntax does not support implicit lifetime elision for types other than & and &mut references. If your method uses types with elided lifetimes (like a type alias type Elided<'a> = &'a usize), you must explicitly name the lifetime or use the '_ placeholder.

    Incorrect:

    trait Test {
        async fn test(not_okay: Elided, okay: &usize) {}
    }

    Correct (using named lifetime):

    trait Test {
        async fn test<'e>(elided: Elided<'e>) {}
    }

    Correct (using '_ placeholder):

    trait Test {
        async fn test(elided: Elided<'_>) {}
    }
  5. Support for non-threadsafe futures with `#[async_trait(?Send)]`

    master

    By default, #[async_trait] assumes that the futures returned by the trait methods must be Send. If you need to work with non-threadsafe futures (e.g., when you cannot or do not want to require Send or Sync bounds on your types), you can use the ?Send feature flag.

    Apply #[async_trait(?Send)] to both the trait definition and the implementation blocks to avoid requiring Send bounds on the async trait methods.

  6. Supported features of `#[async_trait]`

    master

    The #[async_trait] macro supports most standard Rust trait features, including:

    • Self by value, by reference, by mut reference, or no self
    • Any number of arguments and any return value
    • Generic type parameters and lifetime parameters
    • Associated types
    • Mixing async and non-async functions in the same trait
    • Default implementations provided by the trait
    • Elided lifetimes (when explicitly handled)
  7. Handling elided lifetimes in async traits

    master

    Async function syntax in Rust does not allow implicit lifetime elision for types other than & and &mut. When using #[async_trait], you must explicitly name lifetimes or use the anonymous lifetime placeholder '_ for any types that rely on elision.

    If you encounter the error error[E0726]: implicit elided lifetime not allowed here, you must update your function signature.

    Incorrect:

    type Elided<'a> = &'a usize;
    
    #[async_trait]
    trait Test {
        async fn test(not_okay: Elided, okay: &usize) {}
    }

    Correct (using '_):

    #[async_trait]
    trait Test2 {
        async fn test(elided: Elided<'_>, okay: &usize) {}
    }

    Correct (using named lifetimes):

    #[async_trait]
    trait Test {
        async fn test<'e>(elided: Elided<'e>) {}
    }
  8. Use `#[async_trait(?Send)]` for non-threadsafe futures

    master

    By default, #[async_trait] adds a Send bound to the returned futures, requiring that the trait and its implementations are thread-safe. If you do not need your futures to be Send (e.g., you are not using them across thread boundaries), you can opt out of this requirement by using the ?Send argument.

    Apply #[async_trait(?Send)] to both the trait definition and the implementation blocks.