Asynchronous wrapper function taking a reference

Because you annotate 'tr on the function, which means the variable tr in the body needs to live for that long. So a HRTB, i.e. Fun: for<'tr> FnOnce(&'tr mut Db::Transaction) -> Fut, is required to express we want a tr with any lifetime in the body instead of one with a specified lifetime outside the function. Rust Playground

Then you'll run into another problem

error: lifetime may not live long enough
 --> src/main.rs:9:24
  |
9 |     transact(&db, |tx| tx.calculation()).await.unwrap();
  |                    --- ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
  |                    | |
  |                    | return type of closure `impl Future<Output = Result<i32, anyhow::Error>>` contains a lifetime `'2`
  |                    has type `&'1 mut TestTransaction`

It's derived from the lifetime in async fn. Like

    async fn calculation(&mut self) -> Result<i32> {
        Ok(42)
    }

// desugar to
    fn calculation<'1>(&'1 mut self) -> impl '1 + Future<Output = Result<i32>> {
        async move { Ok(42) }
    }

So to change the default behaviour, you can do this Rust Playground

    fn calculation(&mut self) -> impl /* 'static + */ Future<Output = Result<i32>> { // lifetime elision :)
        async move { Ok(42) }
    }

But most of the time, you indeed need the default behaviour, since you use self there Rust Playground

    fn calculation(&mut self) -> impl /* 'static + */ Future<Output = Result<i32>> {
        async move { self; Ok(42) }
    }

// error :(
error[E0700]: hidden type for `impl Future<Output = Result<i32, anyhow::Error>>` captures lifetime that does not appear in bounds
  --> src/main.rs:60:9
   |
59 |     fn calculation(&mut self) -> impl /* 'static + */ Future<Output = Result<i32>> {
   |                    --------- hidden type `[async block@src/main.rs:60:9: 60:36]` captures the anonymous lifetime defined here
60 |         async move { self; Ok(42) }
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
help: to declare that `impl Future<Output = Result<i32, anyhow::Error>>` captures `'_`, you can add an explicit `'_` lifetime bound
   |
59 |     fn calculation(&mut self) -> impl /* 'static + */ Future<Output = Result<i32>> + '_ {
   |                                                                                    ++++

Then this is a common issue with async callbacks. Search in this forum [1] and you'll find many similar solution. Rust Playground

I won't repeat explaning here and there. If one is really curious about things, there are enough excellent explanations given by other masters.


  1. e.g. this recent one ↩︎

3 Likes