Lifetimes in asynchronous closure

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.

2 Likes