How will futures::StreamExt::filter work with async closures?
Background
The futures::StreamExt::filter function is currently defined as this:
fn filter<Fut, F>(self, f: F) -> Filter<Self, Fut, F>
where
F: FnMut(&Self::Item) -> Fut,
Fut: Future<Output = bool>,
The closure f receives a reference to each item in the stream, returning a Future<Output = bool> that tells us if the item should be filtered.
The Problem
As things stand now, this function isn't very useful. To understand why, pretend we call this filter function on a stream of Strings:
.filter(|text| async {text.len() > 2})
When trying to compile this, Rust will complain that the referenced text variable might not live as long as the async block. Thinking about it a bit, this makes sense. It could take an arbitrary amount of time for the returned Future (our async block) to be polled, and in the meantime the referenced text might have been dropped.
Rust currently doesn't have a way to associate the lifetime of the returned future with the lifetime of the input parameters, forcing us to use a number of workarounds.
The Workarounds
Do the computation outside of the async block
This method works when you're only doing synchronous computations on the borrowed argument. It breaks down if you want to pass the text arg to an async function.
.filter(|text| {
let result = text.len() > 2;
async {result}
})
\--or--
.filter(|text| futures::future::ready(text.len() > 2))
Use filter_map instead of filter
This is the approach I'd generally recommend, as it's still easy to reason about and works well with any async computations.
.filter_map(|text| async {
if text.len() > 2 {
Some(text)
} else {
None
}
})
My Questions
As far as I've been able to find online, async closures should resolve this issue and others like it, allowing us to simply do this:
.filter(async |text| text.len() > 2)
My questions are related to how async closures will work.
- Is it accurate that async closures will resolve this issue?
- Will async closures also enable us to work with captured references? (As opposed to arguments that are references)
- If either of the above are true, how will this work?
- More generally, how are async closures intended to be different from the current async block method?
- If async closures don’t fix the above filter function, is there a better alternative?
Thanks so much for anyone that takes the time to read and respond!