I am trying to implement a generic async retry method taking a call with a mutable parameter (in my case database connection), but running into compile errors around lifetimes that I don't completely understand.
Here is a simplified example illustrating the problem:
use std::future::Future;
#[tokio::main]
async fn main() {
let mut foo = Foo { x: 1 };
retry(&mut foo, |f| test(f)).await;
}
async fn retry<C, T, F>(foo: &mut Foo, mut call: C) -> Result<T, ()>
where
C: FnMut(&mut Foo) -> F,
F: Future<Output=Result<T, ()>>,
{
for _ in 0..3 {
match call(foo).await {
Ok(x) => return Ok(x),
Err(_) => ()
}
}
Err(())
}
struct Foo {
x: i32
}
async fn test(foo: &mut Foo) -> Result<i32, ()> {
Ok(foo.x.clone())
}
gives:
error: lifetime may not live long enough
--> src/lib.rs:6:25
|
6 | retry(&mut foo, |f| test(f)).await;
| -- ^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ||
| |return type of closure `impl Future<Output = Result<i32, ()>>` contains a lifetime `'2`
| has type `&'1 mut Foo`
Adding lifetime to foo
in retry
method doesn't help:
async fn retry<'a, C, T, F>(foo: &'a mut Foo, mut call: C) -> Result<T, ()>
where
C: FnMut(&'a mut Foo) -> F,
F: Future<Output=Result<T, ()>>,
{
for _ in 0..3 {
match call(foo).await {
Ok(x) => return Ok(x),
Err(_) => ()
}
}
Err(())
}
gives:
error[E0499]: cannot borrow `*foo` as mutable more than once at a time
--> src/lib.rs:15:20
|
9 | async fn retry<'a, C, T, F>(foo: &'a mut Foo, mut call: C) -> Result<T, ()>
| -- lifetime `'a` defined here
...
15 | match call(foo).await {
| -----^^^-
| | |
| | `*foo` was mutably borrowed here in the previous iteration of the loop
| argument requires that `*foo` is borrowed for `'a`