0

In the following code snippet, a wrapper function takes a callback, executes it and returns its result. This works well in sync code, but breaks in async code.

Sync version (works):

use std::future::Future;

fn wrapper<T, R>(
    mut value: T,
    f: impl for<'a> FnOnce(&'a mut T) -> R,
) -> R
{
    let result = f(&mut value);
    result
}

async fn func(a: &str) -> String {
    wrapper(a, |a|
        a.to_string()
    )
}

playground

Async Version (fails):

use std::future::Future;

async fn wrapper<T, R, F>(
    mut value: T,
    f: impl for<'a> FnOnce(&'a mut T) -> F,
) -> R
where
    F: Future<Output = R>,
{
    let result = f(&mut value).await;
    result
}

async fn func(a: &str) -> String {
    wrapper(a, |a| async move {
        a.to_string()
    }).await
}

playground

Error:
error: lifetime may not live long enough
  --> src/lib.rs:15:20
   |
15 |       wrapper(a, |a| async move {
   |  _________________--_^
   | |                 ||
   | |                 |return type of closure `[async block@src/lib.rs:15:20: 17:6]` contains a lifetime `'2`
   | |                 has type `&'1 mut &str`
16 | |         a.to_string()
17 | |     }).await
   | |_____^ returning this value requires that `'1` must outlive `'2`

error: could not compile `playground` (lib) due to previous error

It seems that the borrow checker doesn't allow the async move closure to capture the a reference, even though the closure execution finishes executing before wrapper.await returns and wrapper.await is happening fully within the lifetime of a, therefore the a reference will outlive the lifetime of the closure. What is going on here? And is there a way to fix it?

Note: I'm aware that it would work if I put the a.to_string() call outside of the async move closure and only capture the String by value, but that's not the goal. This is a toy example and in my real world example, there are several await calls happening within the closure and I can't move that line out of the closure. Also, to_string isn't actually to_string, but a function with side effects that cannot be reordered.

Heinzi
  • 5,793
  • 4
  • 40
  • 69

0 Answers0