Clone variable in Fn async closure

Hello, I don't understand why cloning a variable works in a regular closure, but not in an async closure:

let route_get_mix_confirm = warp::path("get-mix-confirm")
    .and(warp::post())
    .and(warp::body::bytes())
    .and(warp::body::content_length_limit(REQUEST_MAX_SIZE))
    .map({
        let dbs = dbs.clone();
        let indexes = indexes.clone();
        move |raw: Bytes| {
            bincode::serialize(&serve_get_mix_confirm(dbs.clone(), indexes.clone(), raw))
                .unwrap()
        }
    });

let route_get_mix_confirm_blocking = warp::path("get-mix-confirm-blocking")
    .and(warp::post())
    .and(warp::body::bytes())
    .and(warp::body::content_length_limit(REQUEST_MAX_SIZE))
    .map({
        let dbs = dbs.clone();
        let indexes = indexes.clone();
        async move |raw: Bytes| {
            bincode::serialize(&serve_get_mix_confirm_blocking(dbs.clone(), indexes.clone(), raw).await)
                .unwrap()
        }
    });

The async version makes an error: closure is `FnOnce` because it moves the variable `dbs` out of its environment.

Why this, and how to makes it work?

I see that you are using the unstable async closure feature. I would avoid that. Instead use an ordinary closure, that:

  1. first clones dbs
  2. Then moves the clone into an async block.
  3. Returns the async block.

With your solution this part makes no more error, but warp::serve is unhappy:

let route_get_mix_confirm_blocking = warp::path("get-mix-confirm-blocking")
    .and(warp::post())
    .and(warp::body::bytes())
    .and(warp::body::content_length_limit(REQUEST_MAX_SIZE))
    .map({
        let dbs = dbs.clone();
        let indexes = indexes.clone();
        move |raw: Bytes| {
            let dbs = dbs.clone();
            let indexes = indexes.clone();
            async move {
                bincode::serialize(&serve_get_mix_confirm_blocking(dbs, indexes, raw).await)
                    .unwrap()
            }
        }
    });

warp::serve(route_get_mix_confirm_blocking).run(config.listen).await;
// Error:   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reply` is not implemented for `impl Future`

I'm pretty sure you need to call something else than map when it is async, but I don't know what the right one is. Maybe then?

1 Like

Thank you, it works!

let route_get_mix_confirm_blocking = warp::path("get-mix-confirm-blocking")
    .and(warp::post())
    .and(warp::body::bytes())
    .and(warp::body::content_length_limit(REQUEST_MAX_SIZE))
    .and_then({
        let dbs = dbs.clone();
        let indexes = indexes.clone();
        move |raw: Bytes| {
            let dbs = dbs.clone();
            let indexes = indexes.clone();
            async move {
                Result::<Vec<u8>, std::convert::Infallible>::Ok(
                    bincode::serialize(
                        &serve_get_mix_confirm_blocking(dbs, indexes, raw).await,
                    )
                    .unwrap(),
                )
            }
        }
    });

I think this should be in the example, for people unfamiliar with async...

2 Likes

You can always open a PR to add it :slight_smile:

1 Like