You can name anonymous Future from async fn without dyn or Box!

Overview

rename-future

You can name anonymous Future from async fn without dyn or Box!

PLEASE READ THIS

THIS PROJECT NOT YET WELL TESTED! DON'T USE THIS IN PRODUCTION CODE!

What is the problem of async fn and its returning Future?

The return type of async fn is anonymous. means, it is really hard to move around Future of async fn unless type_alias_impl_trait stabilizes. for example, most Service design requires Future as associated type.

Simple example with tower::Service.

impl Service<Request> for AsyncFnService {
    type Response = usize;
    type Error = ();
    type Future = impl Future<Output = Result<Self::Response, Self::Error>>; // ERROR! not allowed until `type_alias_impl_trait` stablizes

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request) -> Self::Future {
        async { 10 }
    }
}

To solve this problem, we need trait boxing. which means make extra runtime costs. and bad, long, ugly type signature something like this.
This makes boxing itself is more expensive then function itself.

impl Service<Request> for AsyncFnService {
    type Response = usize;
    type Error = ();
    type Future = Pin<Box<dyn Future<Output = Result<Self::response, Self::Error> + Send + 'static>>; // LONG AND UGLY!! also makes vtable and heap allocation!

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request) -> Self::Future {
        Box::pin(async { 10 })
    }
}

Using rename-future

With rename-future, you can simply define a new name for returning future! without any runtime costs.
Only you have to do is define a new async fn and add attribute.

impl Service<Request> for AsyncFnService {
    type Response = ();
    type Error = ();
    type Future = FooAsyncFnFuture; // simply use renamed Future! no extra costs!

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request) -> Self::Future {
        foo()
    }
}

#[rename_future(FooAsyncFnFuture)]
async fn foo() -> usize {
    10
}

You also can pass references via adding lifetimes.

#[rename_future(FooAsyncFnFuture)]
async fn add_10<'a>(v: &'a usize) -> usize {
    *v + 10
}

Lifetime will be always required because new defined Future will always require an explicit lifetime.
The signature of FooAsyncFnFuture will look like this.

struct FooAsyncFnFuture<'a> {
    /* private fields */
}

Currently, we have no way to eval Send trait check into const value because current rust compiler does not support generic specialization. So rename-future will require Send for return Future of async fn unless using a !Send marker on attribute.

#[rename_future(FooAsyncFnFuture(!Send))]
async fn foo() -> usize {
    10
}

How does it work?

We create exact same size and aligned named struct on macro and transmute it. at the end, when poll is called on new named future. Pin<&mut Self> is transmutted into original function's return Pin<&mut {Some_Anon_Original_Future}>. and original poll will be called. everything will be inlined so it will just work like holding original Future. without any costs.

this is original function

#[rename_future(AsyncFnFuture)]
async fn async_fn() -> usize {
    10
}

and this is how its look like after macro expansion!

pub const fn __internal_async_fn_sof<F, Fut>(_: &F) -> usize
where
    F: Fn() -> Fut,
{
    std::mem::size_of::<Fut>()
}
pub const fn __internal_async_fn_aof<F, Fut>(_: &F) -> usize
where
    F: Fn() -> Fut,
{
    std::mem::align_of::<Fut>()
}
struct AsyncFnFuture(
    (
        [u8; __internal_async_fn_sof::<_, _>(&__internal_async_fn)],
        rename_future::Align<{ __internal_async_fn_aof::<_, _>(&__internal_async_fn) }>,
    ),
    std::marker::PhantomData<()>,
    std::marker::PhantomPinned,
);
async fn __internal_async_fn() -> usize {
    10
}
fn async_fn() -> AsyncFnFuture {
    impl std::future::Future for AsyncFnFuture {
        type Output = usize;
        fn poll(
            self: std::pin::Pin<&mut Self>,
            cx: &mut std::task::Context<'_>,
        ) -> std::task::Poll<Self::Output> {
            fn call_poll<__T, __Q, __F>(
                _: &__T,
                fut: std::pin::Pin<&mut __F>,
                cx: &mut std::task::Context<'_>,
            ) -> std::task::Poll<__F::Output>
            where
                __T: Fn() -> __Q,
                __Q: std::future::Future<Output = __F::Output>,
                __F: std::future::Future,
            {
                let fut: std::pin::Pin<&mut __Q> = unsafe { std::mem::transmute(fut) };
                fut.poll(cx)
            }
            call_poll::<_, _, _>(&__internal_async_fn, self, cx)
        }
    }
    unsafe { std::mem::transmute(__internal_async_fn()) }
}

Everything is safe under those conditions.

  1. New Future has same size, alignment, lifetime, trait as original Future
  2. New Future is always !Unpin
  3. New Future should be transmutted into exact original Future that it was when its polled.

Limitations

Currently, rename-future does not support async fn with generic types. because current rust compiler cannot eval size or align of type when it has generic types. you can use it by enabling generic_const_exprs nightly feature if you want. but this is not supported on stable version of rust. Also, rename-future does not support impl Trait return type. supporting impl Trait return type means type_alias_impl_trait is stabilized! which makes this crate useless.

You might also like...
A stack-allocated box that stores trait objects.

This crate allows saving DST objects in the provided buffer. It allows users to create global dynamic objects on a no_std environment without a global allocator.

A box full of utilities, a unworthy replacement for coreutils / busybox / toybox.

Gearbox A box full of utilities, a unworthy replacement for coreutils / busybox / toybox. List of content How to setup Systems Ubuntu Arch How to buil

Use winit like the async runtime you've always wanted

async-winit Use winit like the async runtime you've always wanted. winit is actually asynchronous, contrary to popular belief; it's just not async. It

🗑Async-dropper is probably the least-worst ad-hoc AysncDrop implementation you've seen so far.

🗑 async-dropper async-dropper is probably the least-worst ad-hoc AsyncDrop implementation you've seen, and it works in two ways: async_dropper::simpl

The lambda-chaos-extension allows you to inject faults into Lambda functions without modifying the function code.
The lambda-chaos-extension allows you to inject faults into Lambda functions without modifying the function code.

Chaos Extension - Seamless, Universal & Lightning-Fast The lambda-chaos-extension allows you to inject faults into Lambda functions without modifying

Rust Shop is a fake cloud-based software company that you can fork.

RustShop RustShop is an attempt at building a template and utilities to help quickly set up and manage a production grade cloud-based system. The core

Searchbuddy is a browser extension that lets you chat with people that are searching for what you're searching for.
Searchbuddy is a browser extension that lets you chat with people that are searching for what you're searching for.

searchbuddy Make friends while searching! Searchbuddy is a browser extension that lets you chat with people that are searching for what you're searchi

Flexcord! A custom Discord client to allow you to do what you want!

Disclaimer Flexcord is NO WHERE near done. Flexcord What is it? Flexcord is a Discord client that flexes for your needs, it allows you to do exactly w

Bongo Copy Cat wants to be involved in everything you do but instead just imitates you hitting your keyboard all day. After all it's just a cat.
Bongo Copy Cat wants to be involved in everything you do but instead just imitates you hitting your keyboard all day. After all it's just a cat.

Bongo Copy Cat Introduction Bongo Copy Cat wants to be involved in everything you do but instead just imitates you hitting your keyboard all day. Afte

Releases(v0.0.1)
Owner
Jun Ryung Ju
Jun Ryung Ju
An iterator adapter to peek at future elements without advancing the cursor of the underlying iterator.

multipeek An iterator adapter to peek at future elements without advancing the cursor of the underlying iterator. Check out the documentation for more

Luca Palmieri 20 Jul 16, 2022
Rust Stream::buffer_unordered where each future can have a different weight.

buffer-unordered-weighted buffer_unordered_weighted is a variant of buffer_unordered, where each future can be assigned a different weight. This crate

null 15 Dec 28, 2022
messloc is a drop in replacement for malloc that can transparently recover from memory fragmentation without any changes to application code.

messloc is a drop in replacement for malloc that can transparently recover from memory fragmentation without any changes to application code. Goals Al

null 11 Dec 10, 2022
🌌⭐ Git tooling of the future.

❯ Glitter Git tooling of the future. ❯ ?? Features Config files Fast Easy to use Friendly errors ❯ ?? Documentation For proper docs, see here ❯ ✋ What

Milo 229 Dec 22, 2022
Lupus is a utility to administer backups with future integration with rsync

Lupus is a utility to administer backups with future integration with rsync. Many other features are either included or planned such as chat bridges using rcon and or parsing the pipe output from programs/games.

null 3 Sep 19, 2022
A future version of Pokétwo

poketwo-next Pokétwo brings the Pokémon experience to Discord. Catch randomly-spawning pokémon in your servers, trade them to expand your collection,

Pokétwo 13 Aug 20, 2022
Alternative future adapters that provide cancel safety.

cancel-safe-futures Alternative futures adapters that are more cancel-safe. What is this crate? The futures library contains many adapters that make w

Oxide Computer Company 12 Jul 2, 2023
A small tool to clone git repositories to a standard location, organised by domain name and path.

A small tool to clone git repositories to a standard location, organised by domain name and path. Runs on BSD, Linux, macOS, Windows, and more.

Wesley Moore 68 Dec 19, 2022
Booru software for the 21st century. (Name is supposed to be like Puro, the big monster, but I failed..)

Pooru Booru software for the 21st century. Setup Setup is a little funky, but I hope to fix this funkyness down the road. First and foremost, you will

null 2 May 8, 2022
A snapshot of name squatting on crates.io

Machine-readable database of public packages on crates.io which meet an arbitrary, unwritten, sensible definition of name squatting: squatted.csv Form

David Tolnay 69 Feb 1, 2023