There is actually a way to do this which is discussed at Higher function taking async reference-taking closures.
In short you need to define a trait that specifies both the output type and the future type:
pub trait AsyncFnOnce<A> {
type Output;
type Future: Future<Output = Self::Output>;
fn call(self, arg: A) -> Self::Future;
}
Then make a blanket implementation for closures that have the correct signature:
impl<A, C, F> AsyncFnOnce<A> for C
where
C: FnOnce(A) -> F,
F: Future,
{
type Output = F::Output;
type Future = F;
fn call(self, arg: A) -> Self::Future {
self(arg)
}
}
The in_browser
function can then have the following signature:
pub async fn in_browser<F>(f: F)
where
F: for<'a> AsyncFnOnce<&'a WebDriver, Output = WebDriverResult<()>>,
This does currently have some limitations since the compiler isn't smart enough to infer a closures type if an Fn
trait isn't used as a bound on the function that uses the closure. Therefore the above method can only be given functions and not closures. So the following would work while the original in_browser
call using a closure wouldn't:
async fn explict_callback(driver: &WebDriver) -> WebDriverResult<()> {
// do something with the borrowing
driver.get("http://localhost:8000/newShortcut").await?;
Ok(())
}
in_browser(explict_callback).await;
Here is a playground that mocks the problem and here is another playground which "solves" it using the above method.