3

I want to write some generic retry logic for a future.

I know the concrete return type and want to retry the same future.

My code only has access to the future - I do not want to wrap every fn call site in a closure to enable recreating it.

It seems that a "future" is a combination of (fn, args), and when .await is called, it runs and waits for the result in place.

If I am able to clone all of the args, would it be possible to create a clone of the not-started future to retry it if it fails the first time?

zino
  • 1,222
  • 2
  • 17
  • 47

2 Answers2

5

The problem is that a not-yet-started future is the same type as a future that has already started - the future transforms itself in-place. So while in theory a Future could be Clone, that would place severe constraints on the state it's allowed to keep during its whole lifetime. For futures implemented with async fn not only would the initial state (the parameters passed to async fn) have to be Clone, but also so would all the local variables that cross .await points.

A simple experiment shows that the current async doesn't auto-implement Clone the way it does e.g. Send, even for async functions where that would be safe. For example:

async fn retry(f: impl Future + Clone) {
    todo!()
}

fn main() {
    // fails to compile:
    retry(async {});
    //    ^^^^^^^^ the trait `Clone` is not implemented for `impl Future`
}

I do not want to wrap every fn call site in a closure to enable recreating it.

In this situation that's probably exactly what you need to do. Or use some sort of macro if the closure requires too much boilerplate.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
2

A Future can be cloned via https://docs.rs/futures/latest/futures/future/trait.FutureExt.html#method.shared. This is useful to pass the future to multiple consumers, but not suitable for retry.

To have retries with Futures, you need some kind of Future factory, to create a new Future for a retry when an error occurs. Ideally this retry mechanism would be wrapped in its own Future, to hide the complexity for consumers.

There's a crate which does that already: https://docs.rs/futures-retry/latest/futures_retry/struct.FutureRetry.html

Pascalius
  • 14,024
  • 4
  • 40
  • 38