How to use &self inside an async closure

I made a struct that executes a closure with its mutable reference.

struct Connection();

impl Connection {
    async fn exec_closure<T, F>(&mut self, f: F) -> Result<T, ()>
    where
        T: Send,
        F: FnOnce(&mut Self) -> BoxFuture<'_, Result<T, ()>> + Send
    {
        f(self).await
    }
}

And I want to call exec_closure from another struct and use &self inside the closure.

struct Repository();

impl Repository {
    ...
    async fn use_connection(&self) -> Result<(), ()> {
        let mut conn: Connection = self.get_connection().await?;
        conn.exec_closure(|conn| async move {
            self.do_something_else().await
        }.boxed()).await
    }
}

But I get the following error which I cannot understand.

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:29:29
   |
29 |     async fn use_connection(&self) -> Result<(), ()> {
   |                             ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the method body at 29:29...
  --> src/main.rs:29:29
   |
29 |     async fn use_connection(&self) -> Result<(), ()> {
   |                             ^
note: ...so that the expression is assignable
  --> src/main.rs:29:29
   |
29 |     async fn use_connection(&self) -> Result<(), ()> {
   |                             ^^^^^
   = note: expected `&Repository`
              found `&Repository`
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the body at 31:27...
  --> src/main.rs:31:27
   |
31 |           conn.exec_closure(|conn| async move {
   |  ___________________________^
32 | |             self.do_something_else().await
33 | |         }.boxed()).await
   | |_________________^
note: ...so that the expression is assignable
  --> src/main.rs:31:34
   |
31 |           conn.exec_closure(|conn| async move {
   |  __________________________________^
32 | |             self.do_something_else().await
33 | |         }.boxed()).await
   | |_________________^
   = note: expected `Pin<Box<dyn futures::Future<Output = Result<(), ()>> + std::marker::Send>>`
              found `Pin<Box<dyn futures::Future<Output = Result<(), ()>> + std::marker::Send>>`

playground link

Thanks for your help.

P.S. Lifetime + async is really confusing to me, so if you could give me some tutorials or articles on it, that would be great.

I think you posted the wrong playground link.

Thanks! Fixed it.

Would it be:

impl <'a> Connection {
    async fn exec_closure<T, F>(&'a mut self, f: F) -> Result<T, ()>
    where
        T: Send,
        F: FnOnce(&mut Self) -> BoxFuture<'a, Result<T, ()>> + Send
    {
        f(self).await
    }
}
1 Like

That works in the previous example, but won't work if I use conn inside f.

conn.exec_closure(|conn| async move {
    conn.use_mut_ref();
    self.do_something_else().await
}.boxed()).await
error: lifetime may not live long enough
  --> src/main.rs:35:34
   |
35 |           conn.exec_closure(|conn| async move {
   |  ____________________________-----_^
   | |                            |   |
   | |                            |   return type of closure is Pin<Box<(dyn futures::Future<Output = Result<(), ()>> + std::marker::Send + '2)>>
   | |                            has type `&'1 mut Connection`
36 | |             conn.use_mut_ref();
37 | |             self.do_something_else().await
38 | |         }.boxed()).await
   | |_________________^ returning this value requires that `'1` must outlive `'2`

updated playground link

You can't use a lifetime from impl with &mut Self, because that requires a very long-lived lifetime (as long as the whole lifetime of the whole object), which will be absolutely paralyzing for usage of the type.

In this case

F: for<'a> FnOnce(&'a mut Self) -> BoxFuture<'a, Result<T, ()>> + Send

is needed. The for<'a> will create a new lifetime for each function call, so it won't "lock" Self forever.

2 Likes

As a side note, this must have been discussed before, the error message displayed by the compiler is probably in need of improvement:

   = note: expected `&Repository`
              found `&Repository`

and similarly

   = note: expected `Pin<Box<dyn futures::Future<Output = Result<(), ()>> + std::marker::Send>>`
              found `Pin<Box<dyn futures::Future<Output = Result<(), ()>> + std::marker::Send>>`

How do you read this, anyway? Should the first one be something like:

   = note: expected `&'_1 Repository`
              found `&'_2 Repository`

and then some note that '_1 and '_2 aren't compatible with each other?

1 Like

As a second observation, what's the purpose of writing |conn| async move { }.
I think this applies the async and move keywords to the following block whereas you likely want for them to apply to the closure, as in async move |conn| { }.

Thanks.
I've updated the lifetime part, but I'm still getting the same error and can't find a solution.

playground link3

That's where I've managed to get it:

1 Like

I found that it works without Mutex as long as #![feature(nll)] is enabled. -> playground link 4

Unfortunately, I'm trying to do this with an app that can only use the stable version of Rust, but this may not be possible with the stable version?

For either of the last two playground links, if I comment out #![feature(nll)] and run on stable things appear to work the same. Is there a problematic piece left out of the playgrounds?


Beyond that, does #![feature(nll)] actually enable anything in edition 2018 anymore? (Note that async fn isn't available in edition 2015.) The tracking issue seems unclear but I admit I didn't read the backlog; last somewhat informative comment from Niko can be taken to imply there's nothing unstabilized though.

1 Like

I'm not sure what #![feature(nll)] is actually doing, but you're right. The playground code works without it, but I wasn't aware of that.

Thanks!

I thought I didn't need Mutex, but in my actual case which is more like this ↓

async fn exec_closure<'a, T, F>(&'a mut self, f: F) -> Result<T, ()>
where
    T: Send,
    F: FnOnce(&'a Self) -> BoxFuture<'a, Result<T, ()>> + Send
{
    let res = f(self).await;
    self.use_mut_ref().await;
    res
}

I needed to use Mutex to avoid cannot borrow *__self as mutable more than once at a time error.

So thanks for helping me out @kornel! My application code finally worked!!!

playground link of the solution

Sorry for the confusion. The feature(nll) is not needed for this to work. However, it still does one thing: gives different error messages about lifetime issues, so it's helpful in diagnosing tricky cases.

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.