Async function parameter results in Type annotation error

I have a util struct use to retry a function multiple times until it returns an Ok result. There's some Builder methods to configure wait time and retry times, and a function called run that takes in the function to retry.

I have a problem with either the function signature or the return of the lambda method and the compiler error isn't helping me much.

Here is the function in question. The plan is to take in a parameter that is a function that returns a Future. The Future must return a Result of some kind to see if it should be retried or not. This is an async function so that I don't block in-between retries. There might be a few errors in the retry logic but it doesn't matter for now.

pub async fn run_async<T, E, F>(self, func: impl Fn() -> F) -> Result<T, RetryError>
where
    F: Future<Output = Result<T, E>>,
    E: std::fmt::Debug,
{
    let mut current: usize = 0;

    loop {
        let res: Result<T, E> = func().await;

        if res.is_ok() {
            return Ok(res.unwrap());
        } else {
            if self.count == 0 {
                if let Some(wait) = self.waittime.as_ref() {
                    tokio::time::delay_for(*wait).await;
                } else {
                    tokio::task::yield_now().await;
                }

                continue;
            } else {
                current += 1;

                if current > self.count {
                    return Err(RetryError {});
                }
            }
        }
    }
}

I'm calling the methods like this. I'm using future::ok from the Futures crate. I need this in case I need to retry a method that isn't async itself. If I return the return of a function that returns a impl core::future::future::Future the function will compile correctly. But if I return a future from the Futures crate (such as future::ok) it complains about missing type annotations.

let retry_future = Retry::new()
    .max_tries(5)
    .wait(Duration::from_secs(2))
    .run_async( || {
        
        // Sample use case. Does not work.
        return future::ok(1);
    });

Error is :

error[E0282]: type annotations needed
 --> src/main.rs:9:10
  |
6 |     let retry_future = Retry::new()
  |         ------------ consider giving `retry_future` a type
...
9 |         .run_async( || {
  |          ^^^^^^^^^ cannot infer type for type parameter `E` declared on the associated function `run_async`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground`.

I put the sample code in here. I have no idea how to fix this error. The message isn't super helpful. I tried adding type to retry_future but no matter what type I put I can't get the correct one.

Can anyone help me on this ?

The compiler is not able to figure out what the type of E is, since your async block never returns any errors. Try this:

let retry_future = Retry::new()
    .max_tries(5)
    .wait(Duration::from_secs(2))
    .run_async( || {
         async { Result::<_, SomeErrorType>::Ok(1) }
    });

to tell the compiler what the error type should be. Note that you should be using FnMut instead of Fn in this case for the closure, as you do not need to call the closure in parallel.

1 Like

Ah ha ! This is so interesting ! I didn't know you could do this sort of quick async block and return that. That's a million times better. I have now refactored the return of my function with this and it works.

Thanks @Alice !

The futures::future::ok method is a left-over from the pre-async/await days. These days, you can always replace that (and pretty much every other combinator) with an async block.

There's a reason that Tokio doesn't even provide a FutureExt extension trait at all.

2 Likes