Classic. Struggle for hours, post something, then solve it soon after.
So the following solves it, using the implicit lifetime technique. The technique:
Where the problem lied (laid?), change:
async fn graph_try_fold<E, Seed, FnFold>(&self, seed: Seed, fn_fold: FnFold)
where
FnFold: Fn(Seed, &Struct) -> LocalBoxFuture<'_, Result<Seed, E>>,
to:
async fn graph_try_fold<'f, E, Seed, FnFold>(&'f self, seed: Seed, fn_fold: FnFold)
where
for<'f_fold> FnFold:
Fn(Seed, &'f_fold Struct, &'f &'f_fold [(); 0]) -> LocalBoxFuture<'f, Result<Seed, E>>,
which means adding an additional argument in the API consumer's closure:
graph.graph_try_fold(1u16, |seed, r#struct| { .. }
graph.graph_try_fold(1u16, |seed, r#struct, _| { .. }
The solved playpen.
Ideally the API consumer doesn't have the additional argument; the "Improving the ergonomics" part of that post is probably reading material.
Another iteration of the solution:
- no additional param in caller's closure (nicer API)
- stores limit lifetime in the
Graph
structure (less nice?) - no need to Box the future (yay, nicer API)
code
// futures = "0.3.28"
use std::{future::Future, marker::PhantomData, pin::Pin};
use futures::{stream, FutureExt, StreamExt, TryStreamExt};
pub struct Graph<'env>([Struct<'env>; 1]);
pub struct Struct<'env>(PhantomData<&'env ()>);
pub struct Data(u8);
impl<'env> Graph<'env> {
async fn graph_try_fold<'f, E, Seed, FnFold, Fut>(&'f self, seed: Seed, fn_fold: FnFold)
where
FnFold: Fn(Seed, &'f Struct<'_>) -> Fut,
Fut: Future<Output = Result<Seed, E>> + 'f,
{
let fn_fold = &fn_fold;
let _ = stream::iter(self.0.iter())
.map(Result::<_, E>::Ok)
.try_fold(seed, |seed, r#struct| async move {
fn_fold(seed, r#struct).await
})
.await;
}
}
pub trait Trait {
fn trait_fn<'f>(&'f self, data: &'f Data) -> Pin<Box<dyn Future<Output = u16> + 'f>>;
}
impl<'env> Trait for Struct<'env> {
fn trait_fn<'f>(&'f self, data: &'f Data) -> Pin<Box<dyn Future<Output = u16> + 'f>> {
async { data.0.into() }.boxed()
}
}
#[allow(unused_variables)]
pub async fn call_try_fold(graph: &Graph<'_>, data: &Data) {
graph
.graph_try_fold(1u16, |seed, r#struct| async move {
let seed = seed + r#struct.trait_fn(data).await;
Result::<_, ()>::Ok(seed)
})
.await;
}
fn main() {
let _a = call_try_fold(&Graph([Struct(PhantomData)]), &Data(0));
}