3

Please take a look at my code and suggest a possible fix:

use std::future::Future;

pub async fn bar() {
    let mut x = 0u8;
    foo(&mut x, |x| baz(x)).await;
}

pub async fn baz(x: &mut u8) {
    *x += 1;
}

pub async fn foo<'a, F, T>(x: &'a mut u8, f: F)
where
    F: Fn(&'a mut u8) -> T,
    T: Future<Output = ()> + 'a,
{
    loop {
        f(x).await;
    }
}

Why does x remain borrowed after await? What is a proper fix?

ababo
  • 1,490
  • 1
  • 10
  • 24
  • 1
    Your issue is completely unrelated to asynchronous programming. [Here](https://gist.github.com/714fe218eeeafc993cc9f40eda2496b1) is a stripped version that only contains the faulty part of your code. – jthulhu Aug 20 '22 at 11:20
  • 2
    The main difference between your version and mine is that in the latter you can't just drop the lifetime to make it work. – ababo Aug 20 '22 at 13:06
  • You need a HRTB here, but where the lifetime applies to two generic types. I don't think the language has a way to express that currently. – cdhowie Aug 20 '22 at 13:25
  • Can we express that with non-generic future (e.g. Pin>)? – ababo Aug 20 '22 at 14:37

1 Answers1

0

It seems I have found a way to solve the problem with non-generic future type:

use std::future::Future;
use std::pin::Pin;

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;

pub async fn bar() {
    let mut x = 0u8;
    foo(&mut x, baz).await;
}

pub fn baz(x: &mut u8) -> BoxFuture<()> {
    Box::pin(async move {
        baz2(x).await
    })
}

pub async fn baz2(x: &mut u8) {
    *x += 1;
}

pub async fn foo<F>(x: &mut u8, f: F)
where
    F: Fn(&mut u8) -> BoxFuture<()>,
{
    loop {
        f(x).await;
    }
}
ababo
  • 1,490
  • 1
  • 10
  • 24