I'd like to implement the following function, and then have the examples compile:
use std::marker::PhantomData;
use std::future::Future;
struct Invariant<'i>(PhantomData<fn(&'i ()) -> &'i ()>);
fn scoped_async_closure<'i, C, T>(
inv: Invariant<'i>,
c: C
) -> T
where
C: for<'a> FnOnce(Invariant<'i>, &'a u8) -> impl Future<Output = T>
{
let local = 0_u8;
let fut = c(inv, &local);
let t = futures::executor::block_on(fut);
t
}
/////// Examples that should compile
fn example_one<'i>(inv: Invariant<'i>) {
let c = |_inv, loc: &u8| {
let loc = loc;
let fut = async move {
std::future::ready(()).await;
*loc + 1
};
fut
};
let res = scoped_async_closure(inv, c);
assert_eq!(res, 1);
}
fn example_two<'i>(inv: Invariant<'i>) -> Invariant<'i> {
let c = |inv, loc: &u8| async move {
std::future::ready(()).await;
(inv, *loc + 1)
};
let res = scoped_async_closure(inv, c);
assert_eq!(res.1, 1);
res.0
}
Obviously the code above doesn't compile (some of the syntax isn't even real). The intent is just to demonstrate what the API should look like. I should also point out that I'm not sure whether all of the components are important; I don't know whether it matters that T
isn't u8
or that Invariant
is actually invariant. I'm mostly trying not to XY problem myself out of what is already a minimized example.
Getting this to compile won't be easy - any solution that involves making the future type appear as a generic on scoped_async_closure
won't work since then it won't be able to name the 'a
lifetime. Instead, I couldn't figure out anything short of reaching for the full GAT polyfill, with some extra #![feature(unboxed_closures)]
thrown in (because I forget if there was a stable polyfill for naming closure return types in an associated-type way).
This eventually leaves us with this. That's pretty good, but we still get compiler errors on the two examples:
error: lifetime may not live long enough
--> src/lib.rs:79:9
|
74 | let c = |_inv, loc: &u8| {
| - - return type of closure `[async block@src/lib.rs:76:19: 78:10]` contains a lifetime `'2`
| |
| let's call the lifetime of this reference `'1`
...
79 | fut
| ^^^ returning this value requires that `'1` must outlive `'2`
I couldn't make heads or tails of this for a while, until I realized that it's probably the result of the semi-well-known problem with overeager lifetime erasure in generator bodies.
Is there a way to fix this?
I'm fine with unstable features as long as they aren't like... GCE