How to correctly call `async move` closures that accepts reference type as inputs

There are two problems here. The shallow problem is this:

    pub async fn with_data<O, F, Fut>(&self, fun: F) -> O
    where
        F: FnOnce(Vec<&Thing>) -> Fut,
        Fut: Future<Output = O>,
        O: 'static,

When the function is called every type parameter has a single value (a concrete type), chosen by the caller (abstractly if not literally). But here, you want Fut to be able to borrow the Vec<&Thing>, but that Vec has a lifetime which is shorter than the function parameters (because it's a Vec owned by the function body), which is impossible to satisfy by any caller-chosen Fut type.

This particular sub-problem can be solved by using a trait alias for the combination of FnOnce and Future — which the handy async_fn_traits crate provides. You'd use it like this:

    pub async fn with_data<O, F>(&self, fun: F) -> O
    where
        F: for<'a> AsyncFnOnce1<Vec<&'a Thing>, Output = O>,
        O: 'static,

Notice that there now is no Fut type mentioned at all, so it doesn't have to be constrained to be a single parameter.

However, with only this change, the call site still doesn't compile:

error: lifetime may not live long enough
  --> src/main.rs:49:42
   |
49 |           .with_data(|things: Vec<&Thing>| async move {
   |  _________________________________-______-_^
   | |                                 |      |
   | |                                 |      return type of closure `{async block@src/main.rs:49:42: 53:10}` contains a lifetime `'2`
   | |                                 let's call the lifetime of this reference `'1`
50 | |             things.into_iter().for_each(|t| {
51 | |                 dbg!(&t);
52 | |             });
53 | |         })
   | |_________^ returning this value requires that `'1` must outlive `'2`

and I don't know any tricks for solving that problem. Unstable async_closures does solve it:

#![feature(async_closure)]

...
    let _filtered = app
        .with_data(async move |things: Vec<&Thing>| {
            things.into_iter().for_each(|t| {
                dbg!(&t);
            });
        })
        .await;

but perhaps someone else has a better idea for stable.

2 Likes