0

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`

0 Answers0