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.
godmar
July 20, 2021, 9:12am
4
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
kornel
July 20, 2021, 10:19am
6
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
godmar
July 20, 2021, 10:49am
7
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
godmar
July 20, 2021, 11:35am
8
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
kornel
July 20, 2021, 11:45pm
10
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
kornel
July 21, 2021, 12:52pm
15
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
system
Closed
October 19, 2021, 12:52pm
16
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.