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.