Mock an async FnOnce closure that captures at least a reference

pub mod capture_lifetime {
    /// Mock a async FnOnce closure that captures at least a reference.
    pub trait AsyncFnOnce<'a, In, Out> {
        async fn call_once(self, message: In) -> Out;
    }

    #[macro_export]
    macro_rules! async_closure_once {
    (
        { $( $field:ident : $t:ty  = $init:expr ),+ $(,)? };
        async | $( $args:ident  : $a:ty ),+ $(,)? | -> $out:ty
        $e:block
    ) => {{
        struct __AsyncClosure<'a> {
            $( $field: $t , )+
            _ph: std::marker::PhantomData<&'a ()>,
        }
        impl<'a> $crate::capture_lifetime::AsyncFnOnce<'a, ( $($a,)+ ), $out> for __AsyncClosure<'a> {
            async fn call_once(self, __args: ( $($a,)+ )) -> $out {
                let Self { $( $field , )+ .. } = self;
                #[allow(unused_parens)]
                let ( $( $args ),+ , ) = __args;
                $e
            }
        }
        #[allow(clippy::redundant_field_names)]
        __AsyncClosure { $($field: $init , )+ _ph: std::marker::PhantomData }
    }};
    }
}

Usage: Rust Playground

async fn take_a_closure<'a, T, F>(cb: F) -> T
where
    F: for<'s> AsyncFnOnce<'a, (&'s str,), T>,
{
    let s = String::from("-");
    let args = (&s[..],);
    cb.call_once(args).await
}

async fn test() {
    let outer = String::from("+");
    let mut buf = String::new();
    let cb3 = async_closure_once!({
        s: &'a str = &outer,
        buf: &'a mut String = &mut buf,
        u: usize = 123
    }; async |arg: &str| -> std::fmt::Result {
        use std::fmt::Write;
        write!(buf, "{s}{u}{arg}")
    });
    take_a_closure(cb3).await.unwrap();
    assert_eq!(buf, "+123-");
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.