I'm trying to create a generic function that has the impressive goal of wrapping an async closure. So for all intents and purposes, it would treat the closure as a black box and simply run some setup logic before and some logic after, depending on the return value of the closure that is passed in.
Here is an MRE example code I have put together so far. I was also testing it out in the Rust playground as well.
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(())
})
}
}
I would appreciate any help or pointers on how to get this to work with the rust compiler, which doesn't seem too friendly to me here. I feel as I've struggled for hours but am not smart enough or skilled with rust enough to know how to satisfy the compiler rules.
I also checked out Return an async function from a function in Rust and have got the tip to use Box::pin
accordingly. However I'm having trouble implementing it for my specific use case, i.e. where I want to create a generic function to wrap a callable that returns a Future
in particular. Thanks and let me know if I need to clarify on anything.
Update: Thanks to help from @Jakub. Here is my revised (working) code below:
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(())
})
}
}
Side note: it would've been nice to wrap the dyn Future<Output = Result<(), HelpAnError>>
into a generic or perhaps just reuse the F
, but at this point I'm just glad that was able to get it working :-)
CodePudding user response:
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(())
})
}
}