Is it possible to extend the lifetime of any `AsyncFnOnce`?

⚓ rust    📅 2025-05-08    👤 surdeus    👁️ 4      

surdeus

Warning

This post was published 55 days ago. The information described in this article may have changed.

Allowing myself to use a couple of Nightly features, this is the closest I could get to it:

#![feature(async_fn_traits)]
#![feature(unboxed_closures)]

use core::marker::PhantomData;
use core::mem;
use core::pin::Pin;

struct Boxed<'a, F>(F, PhantomData<&'a ()>);

impl<'a, T, U, F> AsyncFnOnce<(T,)> for Boxed<'a, F>
where
    F: AsyncFnOnce(T) -> U,
    F::CallOnceFuture: 'a,
{
    type CallOnceFuture = Pin<Box<dyn Future<Output = U> + 'a>>;
    type Output = U;

    extern "rust-call" fn async_call_once(
        self,
        args: (T,),
    ) -> Self::CallOnceFuture {
        Box::pin(self.0(args.0))
    }
}

struct TypeErased<'a, T, U>(
    Box<
        dyn AsyncFnOnce<
                (T,),
                CallOnceFuture = Pin<Box<dyn Future<Output = U> + 'a>>,
                Output = U,
            > + 'a,
    >,
);

impl<'a, T, U> AsyncFnOnce<(T,)> for TypeErased<'a, T, U> {
    type CallOnceFuture = Pin<Box<dyn Future<Output = U> + 'a>>;
    type Output = U;

    extern "rust-call" fn async_call_once(
        self,
        args: (T,),
    ) -> Self::CallOnceFuture {
        self.0(args.0)
    }
}

unsafe fn extend_lifetime<T, U>(
    fun: impl AsyncFnOnce(T) -> U,
) -> impl AsyncFnOnce(T) -> U + 'static {
    // SAFETY: up to caller.
    unsafe {
        mem::transmute::<TypeErased<'_, T, U>, TypeErased<'static, T, U>>(
            TypeErased(Box::new(Boxed(fun, PhantomData))),
        )
    }
}

But that doesn't compile due to T and U not being 'static, which doesn't make sense to me.

Why should a callback's input and output have to be 'static for it to be 'static?

2 posts - 2 participants

Read full topic

🏷️ rust_feed