#async-trait #async #heap-memory #traits #heap-allocation #future #fork

yanked async-trait-fn

A fork of async-trait with useful additional features

19 releases

0.1.77 Jan 3, 2024
0.1.75 Dec 28, 2023
0.1.74 Oct 15, 2023
0.1.72 Jul 24, 2023
0.1.58 Oct 19, 2022

#37 in #heap-memory

MIT/Apache

56KB
1K SLoC

async-trait-fn

Cargo Crates.io

This is a fork of the widely acclaimed async-trait crate. This crate adds two experimental attributes to async-trait that can be applied to asynchronous trait methods and associated functions to avoid heap memory allocation.

  • The return type of an asynchronous trait method or associated function with the unboxed or unboxed_simple attribute is of impl Future<..> instead of Box<dyn Future<..>>.
  • Those attributes have a lot of limitations when used in a large software product due to known bugs in Rust generic associated types: e.g., 95719 and 90696.

Note that, the main author of async-trait did not want to add such options, therefore this fork will not be merged into the upstream.

See async-trait for more details about async-trait.

Examples

unboxed turns an asynchronous trait method or associated function into a synchronous one returning an impl Future<..> by adding a generic associated type for the method or associated function.

use async_trait_fn::{async_trait, unboxed_simple};

#[async_trait]
pub trait SelfToUsize {
    #[unboxed]
    async fn get(&self) -> usize;
}

#[async_trait]
impl SelfToUsize for u32 {
    #[unboxed]
    async fn get(&self) -> usize {
        *self as usize
    }
}

The compiler generates the following code.

pub trait SelfToUsize {
    fn get<'life0, 'async_trait>(&'life0 self) -> Self::RetTypeOfGet<'life0, 'async_trait>
    where
        'life0: 'async_trait,
        Self: 'async_trait;
    type RetTypeOfGet<'life0, 'async_trait>: ::core::future::Future<Output = usize>
        + ::core::marker::Send
        + 'async_trait
    where
        'life0: 'async_trait,
        Self: 'async_trait,
        Self: 'life0;
}

impl SelfToUsize for u32 {
    fn get<'life0, 'async_trait>(&'life0 self) -> Self::RetTypeOfGet<'life0, 'async_trait>
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        async move {
            if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<usize> {
                return __ret;
            }
            let __self = self;
            let __ret: usize = { *__self as usize };
            #[allow(unreachable_code)]
            __ret
        }
    }
    type RetTypeOfGet<'life0, 'async_trait> = impl ::core::future::Future<Output = usize>
        + ::core::marker::Send
        + 'async_trait
    where
        'life0: 'async_trait,
        Self: 'async_trait,
        Self: 'life0 ;
}

unboxed_simple is identical to unboxed except that it substitutes all the lifetime bounds and parameters with a single, fixed lifetime: 'async_trait. When code around an unboxed attribute does not compile, unboxed_simple might help.

use async_trait_fn::{async_trait, unboxed_simple};

#[async_trait]
pub trait AddOther {
    #[unboxed_simple]
    async fn add<'s, 'o>(&'a self, other: &'o usize) -> usize;
}

#[async_trait]
impl AddOther for u32 {
    #[unboxed_simple]
    async fn add<'s, 'o>(&'a self, other: &'o usize) -> usize {
        (*self as usize) + *other
    }
}

The above code expands to the following code; all the lifetime parameters are replaced with 'async_trait.

pub trait AddOther {
    fn add<'async_trait>(
        &'async_trait self,
        other: &'async_trait usize,
    ) -> Self::RetTypeOfAdd<'async_trait>
    where
        Self: 'async_trait;
    type RetTypeOfAdd<'async_trait>: ::core::future::Future<Output = usize>
        + ::core::marker::Send
        + 'async_trait
    where
        Self: 'async_trait,
        Self: 'async_trait;
}

impl AddOther for u32 {
    fn add<'async_trait>(
        &'async_trait self,
        other: &'async_trait usize,
    ) -> Self::RetTypeOfAdd<'async_trait>
    where
        Self: 'async_trait,
    {
        async move {
            if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<usize> {
                return __ret;
            }
            let __self = self;
            let other = other;
            let __ret: usize = { (*__self as usize) + *other };
            #[allow(unreachable_code)]
            __ret
        }
    }
    type RetTypeOfAdd<'async_trait> = impl ::core::future::Future<Output = usize>
        + ::core::marker::Send
        + 'async_trait
    where
        Self: 'async_trait,
        Self: 'async_trait;
}

Dependencies

~225–660KB
~16K SLoC