So, FWIW, the following works:
#![allow(unused)]
#![warn(unused_must_use)]
use ::{
futures::{
future::{BoxFuture, FutureExt},
},
std::{
collections::HashMap,
future::Future,
},
};
pub
struct ObjectCache<T> {
pub store: HashMap<u32, T>,
}
impl<T> ObjectCache<T> {
pub
fn new ()
-> ObjectCache<T>
{
ObjectCache {
store: <_>::default(),
}
}
pub
async
fn get<'up, F : 'up, R> (
self: &'_ ObjectCache<T>,
handle: u32,
closure: F,
) -> Result<R, ()>
where
for<'r>
F : FnOnce(&'r T, [&'r &'up (); 0])
-> BoxFuture<'r, Result<R, ()>>
,
{
match self.store.get(&handle) {
| Some(obj) => closure(obj, []).await,
| None => Err(())
}
}
}
async
fn perform_action (
request: &'_ str,
handle: u32,
) -> Result<u32, ()>
{
AGENT_CACHE
.get(
handle,
move |agent: &'_ Agent, []| {
agent.process(request)
}.boxed(),
)
.await
}
::lazy_static::lazy_static! {
pub
static ref AGENT_CACHE: ObjectCache<Agent> = {
ObjectCache::<Agent>::new()
};
}
pub
struct Agent;
impl Agent {
pub
async
fn process (
self: &'_ Agent,
request: &'_ str,
) -> Result<u32, ()>
{
// info!("Processing {}", request);
let () = async {}.await;
// sleep(Duration::from_secs(1)).await;
Ok(42)
}
}
It showcases the hack described over
to be able to upper-bound the higher-order lifetime in for<'r> FnOnce(&'r T) -> BoxFuture<'r, …>
(the problem @steffahn was talking about) by adding an extra phony parameter, [&'r &'up (); 0]
(for some "free" generic lifetime parameter <'up>
) so as to yield an implicit where 'up : 'r
bound on the higher-order lifetime, thus allowing to capture stuff outside the closure. It then requires the call sites to provide that extra []
parameter.