I'm trying to write the following generic function:
async fn wait_for<'b, 'a: 'b, F, A, T>(arg: &'a mut A, mut func: F) -> Result<()>
where
F: FnMut(&'b mut A) -> T,
T: Future<Output = Result<bool>> + 'a,
{
let timeout_instant = Instant::now() + TIMEOUT_DURATION;
while Instant::now() < timeout_instant {
if let Ok(boolean) = func(arg).await {
if boolean {
return Ok(());
}
}
log::info!("Waiting {POLL_DURATION:?}");
tokio::time::sleep(POLL_DURATION).await;
}
bail!("Timed out after {POLL_DURATION:?}")
}
To wait for a condition with a timeout. This fails to compile because I cannot convince the compiler that the borrow of arg only lasts for the duration of the function call:
cannot borrow `*arg` as mutable more than once at a time
`*arg` was mutably borrowed here in the previous iteration of the loop
Unfortunately the mutability of the reference cannot be removed. How do I tell the compiler that the borrow of arg only lasts for the duration of func in each loop iteration?
By using a parameter lifetime 'b, from outside this function body, you're promising that F is allowed to use that reference for that entire lifetime. You can't make that promise across multiple calls to F.
You can use a more flexible lifetime with F: for<'b> FnMut(&'b mut A) -> T, so 'b is different between each call. This is the same as what you get if you omit the lifetime, F: FnMut(&mut A) -> T.
Thank you. Unfortunately when I remove the bound, I get the error that I think I was originally trying to resolve, which is helping the compiler understand that the arg outlives the future wait_for returns:
// roughly...
async fn code(
clients: &mut [C]
) -> Result<()> {
for (id, c) in clients.iter_mut().enumerate() {
wait_for(c, |client| async {
my_func(client).await?
}).await?;
}
}
results in
lifetime may not live long enough
returning this value requires that `'1` must outlive `'2`
code.rs(183, 31): has type `&'1 mut C`
code.rs(183, 37): return type of closure `impl futures::Future<Output = std::result::Result<bool, anyhow::Error>>` contains a lifetime `'2`
Small tip on how to ask better questions: Include a complete error message as outputted by running cargo check in the terminal. Also, properly indented code will be a lot more readable.
The practical suggestion thus is to work with boxed futures instead. See the next answer in the same thread I linked above for some examples. In addition to that answer, note that you can also use the futures::FutureExt::boxed method for a post-fix (and slightly less ambiguously typed w.r.t. conversion to the trait object) alternative of using Box::pin(…).