Using the pattern of returning a pinned boxed future from a function, you are able to clone a value being captured.
Playground
use std::pin::Pin;
pub struct Request {}
pub trait HandlerFunc: Send + Sync + Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>> {}
impl<F> HandlerFunc for F
where
F: Send + Sync + Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>>,
{}
fn create_handler(v: String) -> impl HandlerFunc {
move |_req: Request| {
let v = v.clone(); // ⬅️ closure takes ownership of variable
// and explicitly clones it every time
// the closure is called.
Box::pin(async move {
println!("{}", v);
()
})
}
}
#[tokio::main]
async fn main() {
let cb = create_handler("Hello World".to_string());
cb(Request{}).await;
cb(Request{}).await;
}
It would be great to tidy this up using async closures (introduced in 1.85).
fn handler(v: String) -> impl HandlerFunc {
async move |_req: Request| {
println!("{}", v)
}
}
However, when attempting to do this with an async closure, I am unable to clone the incoming value or implicitly rely on Copy
to clone the reference.
Playground
pub struct Request {}
pub trait HandlerFunc:
Sized + Send + Sync + Copy + 'static + Fn(Request) -> Self::OutputFuture
{
type OutputFuture: Future<Output = ()>;
}
impl<F, Fut: Future<Output = ()>> HandlerFunc for F
where
F: Sized + Send + Sync + Copy + 'static + Fn(Request) -> Fut,
{
type OutputFuture = Fut;
}
fn handler(v: i32) -> impl HandlerFunc {
// ⬇️ error: async closure does not implement `Fn` because
// it captures state from its environment
async move |_req: Request| {
println!("{}", v) // i32 implements Copy so it should be cloned
// I have also tried &v and removing "move"
}
}
#[tokio::main]
async fn main() {
let cb = handler(42);
cb(Request {}).await;
cb(Request {}).await;
}
Is it possible to implement this using async closures?
Using the pattern of returning a pinned boxed future from a function, you are able to clone a value being captured.
Playground
use std::pin::Pin;
pub struct Request {}
pub trait HandlerFunc: Send + Sync + Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>> {}
impl<F> HandlerFunc for F
where
F: Send + Sync + Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>>,
{}
fn create_handler(v: String) -> impl HandlerFunc {
move |_req: Request| {
let v = v.clone(); // ⬅️ closure takes ownership of variable
// and explicitly clones it every time
// the closure is called.
Box::pin(async move {
println!("{}", v);
()
})
}
}
#[tokio::main]
async fn main() {
let cb = create_handler("Hello World".to_string());
cb(Request{}).await;
cb(Request{}).await;
}
It would be great to tidy this up using async closures (introduced in 1.85).
fn handler(v: String) -> impl HandlerFunc {
async move |_req: Request| {
println!("{}", v)
}
}
However, when attempting to do this with an async closure, I am unable to clone the incoming value or implicitly rely on Copy
to clone the reference.
Playground
pub struct Request {}
pub trait HandlerFunc:
Sized + Send + Sync + Copy + 'static + Fn(Request) -> Self::OutputFuture
{
type OutputFuture: Future<Output = ()>;
}
impl<F, Fut: Future<Output = ()>> HandlerFunc for F
where
F: Sized + Send + Sync + Copy + 'static + Fn(Request) -> Fut,
{
type OutputFuture = Fut;
}
fn handler(v: i32) -> impl HandlerFunc {
// ⬇️ error: async closure does not implement `Fn` because
// it captures state from its environment
async move |_req: Request| {
println!("{}", v) // i32 implements Copy so it should be cloned
// I have also tried &v and removing "move"
}
}
#[tokio::main]
async fn main() {
let cb = handler(42);
cb(Request {}).await;
cb(Request {}).await;
}
Is it possible to implement this using async closures?
Share Improve this question edited Mar 19 at 23:34 David Alsh asked Mar 19 at 23:08 David AlshDavid Alsh 7,7238 gold badges44 silver badges79 bronze badges 2 |2 Answers
Reset to default 1Async closures don't implement just the Fn*
traits (although they sometimes can be treated as such), but also, and more importantly the AsyncFn*
traits. See Calling a generic async function with a (mutably) borrowed argument for details.
However, there is no way to refer to the future of those traits (except on nightly), so the answer to your question is unfortunately no.
Most likely you want to use some of the AsyncFn*
traits instead of the Fn
trait. But if that is not an option, you can achieve equivalent code to the first example by wrapping the async closure with a utility function (wrap
below) that clones the async closure every time before calling it: (note that this function can be reused, which should make the code better than the first example if the utility function is used multiple times)
fn wrap<A, Fut>(a: A) -> impl HandlerFunc<OutputFuture=Fut>
where
Fut: Future<Output=()>,
A: Clone + Send + Sync + FnOnce(Request) -> Fut,
{
move |request|a.clone()(request)
}
fn handler(v: i32) -> impl HandlerFunc {
wrap(async move |_req: Request| {
println!("{}", v)
})
}
Here is a full example:
pub struct Request {}
pub trait HandlerFunc:
Send + Sync + Fn(Request) -> Self::OutputFuture
{
type OutputFuture: Future<Output = ()>;
}
impl<F, Fut: Future<Output = ()>> HandlerFunc for F
where
F: Send + Sync + Fn(Request) -> Fut,
{
type OutputFuture = Fut;
}
fn wrap<A, Fut>(a: A) -> impl HandlerFunc<OutputFuture=Fut>
where
Fut: Future<Output=()>,
A: Clone + Send + Sync + FnOnce(Request) -> Fut,
{
move |request|a.clone()(request)
}
fn handler(v: i32) -> impl HandlerFunc {
wrap(async move |_req: Request| {
println!("{}", v)
})
}
#[tokio::main]
async fn main() {
let cb = handler(42);
cb(Request {}).await;
}
move |_req: Request| async move { ... }
which will work directly with yourCopy
code – kmdreko Commented Mar 20 at 1:07