0

In what I came up with, the run function fails to compile because the return type of the closure depends on the lifetime of the mutable reference. I can't express the right bounds on F that would fix it. Is it possible?

pub async fn with_mutex<'a, I, O, F, Fut>(mutex: &'a Mutex<I>, f: F) -> O
where
  // F: for<'b> (FnOnce(&'b mut I) -> impl (Future<Output = O> + 'b)),
  F: FnOnce(&mut I) -> Fut + 'a,
  Fut: Future<Output = O> + 'a,
{
  let mut guard = mutex.lock().await;
  f(&mut guard).await
}

pub async fn run() {
  let mutex = Mutex::new(5);
  let fut = with_mutex(&mutex, |value| async {
    *value += 1;
  })
  .await;
}
NioBium
  • 583
  • 3
  • 10

1 Answers1

1

Sadly, Rust's bound syntax isn't yet expressive enough to support an HRTB for a closure whose return type is another generic type bounded on one of the HRTB lifetimes.

The most common workaround is to require the closure return a boxed future. It's not ideal, but usually if you're doing async stuff then whatever you're awaiting is going to be orders of magnitude slower than a heap allocation and the added indirection of a dyn Future.

use std::future::Future;
use futures::lock::Mutex;
use futures::future::BoxFuture;

pub async fn with_mutex<I, O, F>(mutex: &Mutex<I>, f: F) -> O
where
  F: for<'a> FnOnce(&'a mut I) -> BoxFuture<'a, O>,
{
  let mut guard = mutex.lock().await;
  f(&mut guard).await
}

pub async fn run() {
  let mutex = Mutex::new(5);
  let fut = with_mutex(&mutex, |value| Box::pin(async {
    *value += 1;
  }))
  .await;
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300