Once again I attempt something that I find trivial in Scala and end up beating my head against a wall for a half hour trying to do it in Rust.
I just want to write a general fn to use backoff::retry to retry an async operation, as a sync op, meaning running the inner op with task::block_in_place
.
IOW, the operation (f) is just any async fn.
Here's one of the things I've tried:
pub fn run_with_retry<F, B, T, E>(backoff: ExponentialBackoff, f: F) -> Result<T, Error<E>>
where
F: FnMut() -> Future<Output = Result<T, Error<E>>>,
{
retry(backoff, || {
task::block_in_place(move || runtime::Handle::current().block_on(f))
.map_err(backoff::Error::transient)
})
}
The error here:
error[E0782]: expected a type, found a trait
--> util/src/future_runner.rs:33:23
|
33 | F: FnMut() -> Future<Output = Result<T, Error<E>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: you can add the `dyn` keyword if you want a trait object
|
33 | F: FnMut() -> dyn Future<Output = Result<T, Error<E>>>,
Following the compiler directive, and then another one, we get to
pub fn run_with_retry<F, B, T, E>(backoff: ExponentialBackoff, f: F) -> Result<T, Error<E>>
where
F: FnMut() -> (dyn Future<Output = Result<T, Error<E>>>) + std::future::Future,
{
retry(backoff, || {
task::block_in_place(move || runtime::Handle::current().block_on(f))
.map_err(backoff::Error::transient)
})
}
First, I cannot grok why such type bounds are necessary: Why does F have to be both dyn Future
and Future
?
And that still doesn't compile, as map_err
doesn't exist on Future.
Yet, I can do this, directly:
let result = retry(backoff(), || {
block_in_place(|| {
Handle::current().block_on(
<some_async_fn>(),
)
})
.map_err(backoff::Error::transient)
});
Very often I find it difficult to take a bit of Rust code and abstract it into something general for reasons along these lines. It seems the types the compiler is working with (inferring) are often quite complex.
Can someone steer me in the right direction here?