Why don't async move closures implement Fn?

I'm working on an async program (a server) which uses async closures that capture their environment by value (with move) as request handlers. It means they are possibly called multiple times in their lifetime so FnOnce is not enough and they must implement Fn (or FnMut). The problem is that the compiler produces this error:

error: async closure does not implement `Fn` because it captures state from its environment

Here (Rust playground) is a simpler code which produces the same error:

#![feature(async_closure)]

use std::future::Future;

fn give_me_a_fn_async_closure<F, Out>(f: F)
where
    F: Fn() -> Out,
    Out: Future<Output = ()>
{}

fn move_async_closure() {
    let integer: i32 = 42;
    
    let closure = async move || {
        println!("{}", integer);
    };
    
    give_me_a_fn_async_closure(closure);
}

And the error:

   Compiling playground v0.0.1 (/playground)
error: async closure does not implement `Fn` because it captures state from its environment
  --> src/lib.rs:14:19
   |
14 |     let closure = async move || {
   |                   ^^^^^^^^^^^^^
...
18 |     give_me_a_fn_async_closure(closure);
   |     -------------------------- required by a bound introduced by this call
   |
note: required by a bound in `give_me_a_fn_async_closure`
  --> src/lib.rs:7:8
   |
5  | fn give_me_a_fn_async_closure<F, Out>(f: F)
   |    -------------------------- required by a bound in this function
6  | where
7  |     F: Fn() -> Out,
   |        ^^^^^^^^^^^ required by this bound in `give_me_a_fn_async_closure`

I have some workarounds in mind, but I don't understand why does is happen. Why don't async move closures implement Fn?

This is pretty strange, but if you use the nightly async fn traits it works.

#![feature(async_closure, async_fn_traits)]

fn give_me_a_fn_async_closure<F, Out>(_f: F)
where
    F: core::ops::AsyncFn() -> Out
{
}

I think it may be because you can move the async closure while there exist futures that are referencing it.

1 Like

The standard Fn traits don't support returning borrows of the implementer; for example a closure can't return a borrow of something captured by value or a reborrow of something captured by exclusive reference. This is analogous to how the Iterator trait doesn't support lending iterators.

They AsyncFn heirarchy does support such "lending closures"... provided the return type implements Future.[1]


That doesn't really address your OP though, which doesn't need that lending quality. At a guess, they just don't try to implement the Fn traits at all in order to keep their options open. You may be able to hold the compiler's hand by creating your futures across a fn boundary instead of using an async closure or block.


  1. I don't know if there's a good reason for that restriction. ↩ī¸Ž

1 Like

You can also just flip it to || async move { ... } which also forces the integer to be copied.

2 Likes

Thank you guys. I can now understand why it happened.

A slightly different version of what both of you mentioned has solved my problem.
I just had to use a "normal" closure which clones/copies the moved values and returns an async block as a Future like so (I have Arcs which need to be cloned):

move |args: Args| {
   let arc = arc.clone(); // Shadows arc

    async move {
        arc.some_async_fn(args).await
    }
}

This will make each future have it's own copy of the state and thus prevents them to borrow from the closure itself.

Since only one reply can be marked as the solution and both of your replies have been very helpful, I will mark this one as the solution.

2 Likes