I'm designing a connection retry function. It retries if the error was ConnectionClosed
. In the full implementation it has other things that justify it being a recursion, but this is off topic. Anyways, since it's a connection, it makes sense to be async. However, async recursives force me to use BoxFuture
, and we know that asyncs capture and return all the arguments.
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
struct Message;
struct Client;
enum Error {
ConnectionClosed
}
impl Client {
fn send_and_expect<'a>(
&'a mut self,
message: &'a Message
) -> BoxFuture<'a, Result<(), Error>> {
async move {
Ok(())
}.boxed()
}
//With this function I can wrap any async function f that grabs data from the internet
pub fn connection_retrier<'a, T>(
f: fn(&'a mut Self, &'a Message) -> T,
f_self: &'a mut Self,
f_m: &'a Message,
) -> BoxFuture<'a, Result<(),Error>>
where
T: Future<Output = Result<(), Error>> + 'a + Send
{
/*
By making f: fn(&'a mut Self, &'a Message) -> T, we tell the compiler that
`f` is a specific function: one that specifically requires the `Self` and `Message`
arguments to live as long as `'a`, where `'a` is the min of the lifetimes of the `f_self` and `f_m`
arguments passed to `connection_retrier`.
Thus this forces `f_self` and `f_m` to live as long as `connection_retrier`.
The call `let r = f(f_self, f_m).await` returns a `Future` that lives as long as `'a`.
I think this is the problem. The result of the first call borrows `f_self` and `f_m`
for at least the entire lifetime of `r`. This would make it impossible to use
`f_self` and `f_m` again inside `connection_retrier`. As you see, I tried making sure
that `r` destructs before we use `f_self` in `connection_retrier` again, but somehow
`r` is marked to live as long as `connection_retrier`, because `f_self` is still considered
to be borrowed.
*/
async move {
let ok: bool;
{
let r = f(f_self, f_m).await;
match r {
Ok(_) => ok = true,
Err(Error::ConnectionClosed) => {
ok = false;
}
}
}
match ok {
true => Ok(()),
false => Client::connection_retrier(f, f_self, f_m).await
}
}.boxed()
}
async fn send_with_retry<'a> (
&'a mut self,
message: &'a Message,
) -> Result<(), Error>
{
Client::connection_retrier(
Client::send_and_expect,
self,
message
).await
}
}
Error:
error[E0499]: cannot borrow `*f_self` as mutable more than once at a time
--> src/lib.rs:58:56
|
24 | f: fn(&'a mut Self, &'a Message) -> T,
| - lifetime `'1` appears in the type of `f`
...
48 | let r = f(f_self, f_m).await;
| --------------
| | |
| | first mutable borrow occurs here
| argument requires that `*f_self` is borrowed for `'1`
...
58 | false => Client::connection_retrier(f, f_self, f_m).await
| ^^^^^^ second mutable borrow occurs here
I understand why the error occurs: Client::connection_retrier(f, f_self, f_m).await
, let's call it r
, holds a mutable reference to f_self
, so I cannot use it again while it's being held. However, after I check that this result r
is Error::ConnectionClosed
, I don't need it anymore, so there should be a way to discard it so I can reborrow it mutably.