如何创建一个通用函数来包装异步闭包?

How to create a generic function to wrap an async closure?

我正在尝试创建一个通用函数,它具有包装异步闭包的令人印象深刻的目标。因此,出于所有意图和目的,它会将闭包视为 黑盒 并且只是 运行 之前的一些设置逻辑和之后的一些逻辑,具体取决于 return传入的闭包值。

这是我到目前为止整理的 MRE 示例代码。我也在 Rust playground 中对其进行了测试。

use std::future::Future;
use std::pin::Pin;

#[derive(Default)]
struct MyEvent {}

#[derive(Default)]
struct MyContext {
    pub fn_name: String,
}

pub struct HelpAnError {}

#[tokio::main]
async fn main() {
    let my_age: u8 = 29;

    let doing_work_hard = move |event: MyEvent, ctx: MyContext| async move {
        println!("I'm working hard, i promise!");
        println!("Ma i'm already {} yrs old!", my_age);
        Ok::<_, HelpAnError>(())
    };

    let doing_chores_hard = lets_wrap_da_closure(doing_work_hard);

    // does all the chores that Ma assigned to me
    let _ = doing_chores_hard(Default::default(), Default::default()).await;
}

fn lets_wrap_da_closure<
    C: Fn(MyEvent, MyContext) -> F,
    F: Future<Output = Result<(), HelpAnError>>,
>(
    do_work_hard: C,
) -> fn(MyEvent, MyContext) -> Pin<Box<dyn Future<Output = Result<(), HelpAnError>>>> {
    move |event: MyEvent, ctx: MyContext| {
        Box::pin(async move {
            println!("I'm doin my chores Ma, I promise!");
            // **help** - if I uncomment this it fails!
            // do_work_hard(event, ctx).await;
            println!("Yay! The chores are now complit.");
            Ok(())
        })
    }
}

对于如何使它与 rust 编译器一起工作的任何帮助或指示,我将不胜感激,这对我来说似乎不太友好。我觉得我已经奋斗了几个小时,但我不够聪明,也不够熟练,无法知道如何满足编译器规则。

我也检查了 并得到了相应使用 Box::pin 的提示。但是,我在针对我的特定用例实现它时遇到了麻烦,即我想创建一个通用函数来包装一个可调用对象,特别是 return 是一个 Future。谢谢,如果我需要澄清任何事情,请告诉我。


更新:感谢@Jakub 的帮助。下面是我修改后的(工作)代码:

use std::future::Future;
use std::pin::Pin;

#[derive(Default)]
struct MyEvent {}

#[derive(Default)]
struct MyContext {
    pub fn_name: String,
}

#[derive(Debug)]
pub struct HelpAnError {}

#[tokio::main]
async fn main() {
    let my_age: u8 = 29;

    let doing_work_hard = move |event: MyEvent, ctx: MyContext| async move {
        println!("I'm working hard, i promise!");
        println!("Ma i'm already {} yrs old!", my_age);
        Ok::<_, HelpAnError>(())
    };

    let doing_chores_hard = lets_wrap_da_closure(doing_work_hard);

    // does all the chores that Ma assigned to me
    let _ = doing_chores_hard(Default::default(), Default::default()).await;
}

fn lets_wrap_da_closure<
    'a,
    C: Fn(A1, A2) -> F,
    A1,
    A2,
    E: std::fmt::Debug,
    F: Future<Output = Result<(), E>> + 'a,
>(
    do_work_hard: C,
) -> impl Fn(A1, A2) -> Pin<Box<dyn Future<Output = Result<(), E>> + 'a>> {
    move |event: A1, ctx: A2| {
        let fut = do_work_hard(event, ctx);
        Box::pin(async move {
            println!("I'm doin my chores Ma, I promise!");
            // it WORKS! wow, amazing ~
            match fut.await {
                Ok(_) => println!("All systems are a GO!"),
                Err(e) => println!("I ran into issue doing my chores: {:?}", e),
            };
            println!("Yay! The chores are now complit.");
            Ok(())
        })
    }
}

旁注:将 dyn Future<Output = Result<(), HelpAnError>> 包装成通用的或者可能只是重复使用 F 会很好,但在这一点上我很高兴能够让它工作:-)

use std::future::Future;
use std::pin::Pin;

#[derive(Default)]
struct MyEvent {}

#[derive(Default)]
struct MyContext {
    pub fn_name: String,
}

pub struct HelpAnError {}

#[tokio::main]
async fn main() {
    let my_age: u8 = 29;

    let doing_work_hard = move |event: MyEvent, ctx: MyContext| async move {
        println!("I'm working hard, i promise!");
        println!("Ma i'm already {} yrs old!", my_age);
        Ok::<_, HelpAnError>(())
    };

    let doing_chores_hard = lets_wrap_da_closure(doing_work_hard);

    // does all the chores that Ma assigned to me
    let _ = doing_chores_hard(Default::default(), Default::default()).await;
}

fn lets_wrap_da_closure<
    C: Fn(MyEvent, MyContext) -> F + 'static,
    F: Future<Output = Result<(), HelpAnError>> + 'static,
>(
    do_work_hard: C,
) -> impl Fn(MyEvent, MyContext) -> Pin<Box<dyn Future<Output = Result<(), HelpAnError>>>> {
    move |event: MyEvent, ctx: MyContext| {
        // this is the only way i can satisfy the compiler, do not worry,
        // future will get executed only upon await and this call is just 
        // creating the future we are passing
        let fut = do_work_hard(event, ctx);
        Box::pin(async move {
            println!("I'm doin my chores Ma, I promise!");
            // **help** - if I uncomment this it fails!
            drop(fut.await);
            println!("Yay! The chores are now complit.");
            Ok(())
        })
    }
}