How to return FnMut with async move?

After reading several examples of how to return FnMut by ensuring I don't move out any referenced variables, I can't seem to make that happen, even after cloning, referencing, etc the config variable:

pub async fn run_lambda<F>(
    handler: &(impl Fn(SdkConfig, lambda_http::Request) -> F + Sync),
) -> Result<(), lambda_http::Error>
where
    F: Future<Output = Result<Response<Body>, lambda_http::Error>> + Send,
{
    let config = aws_config::defaults(aws_config::BehaviorVersion::latest())
    .load()
    .await;
    let shared_config = config.clone();

    lambda_http::run(lambda_http::service_fn(|event: Request| async move {
        handler(Box::new(config), event)
            .await
            .map_err(|e| lambda_http::Error::from(format!("{e:#?}")))
    }))
    .await
}
29 |     lambda_http::run(lambda_http::service_fn(move |event: Request| async move {
   |     ----------------                         ^^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
   |     |
   |     the requirement to implement `FnMut` derives from here
30 |         handler(shared_config, event)
   |                 ------------- closure is `FnOnce` because it moves the variable `shared_config` out of its environment
   |
   = note: required for `ServiceFn<{closure@src/operation.rs:29:46: 29:67}>` to implement `Service<lambda_http::http::Request<lambda_http::Body>>`

Here is the referenced library:

Try moving the let shared_config = config.clone(); into the closure like

|event: Request| {
    let shared_config = config.clone();
    async move {
     ā€¦ā€¦
    } 
} 

To remove some cloning overhead, you could also wrap the thing in an Arc (e. g. where config is defined). (Unless your use case needs an owned config value, of course.)

2 Likes

Such a simple solution staring right at me and yet I would have never seen it in a million years. Thank you...

Knowing the right kind of places where a clone can help certainly is an acquired skill :wink:

Knowing that the place between a closure and the async block it returns is often a good contender for such a clone of shared (captured) data is also something that note too seldomly useful, and if you don't know it, this scope of putting the let is certainly easy to overlook (since you usually - as is the case here - tend to not have any block for the closure body at all prior to adding the let)

Alternatively, (or additionally), it can also help to learn more about closure captures and closure traits, to make sense of why this fix actually solved the problem.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.