I've encountered a situation where I need to iterate until I encounter an element causing me to stop. Unlike take_while
, I actually want to keep the last "failed" element. I was surprised to find out the standard library doesn't provide an immediate way to do that.
There is a crate for this with quite a few downloads (All-Time: 1,055,965, Recent: 128,680) but I am not a huge fan of std-like micro-dependencies.
I have managed to implement a version that works for me, leaning on scan
:
trait TakeUntilExt: Iterator {
fn take_until<P>(self, predicate: P) -> impl Iterator<Item = Self::Item>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool;
}
impl<I: Iterator> TakeUntilExt for I {
fn take_until<P>(self, predicate: P) -> impl Iterator<Item = Self::Item>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool,
{
let mut predicate = predicate;
let mut done = false;
self.scan((), move |_, item| {
if done {
return None;
}
if !predicate(&item) {
done = true;
}
Some(item)
})
}
}
fn main() {
let iter = [1, 2, 3, -1, -2].into_iter();
println!(
"{:?}",
iter.clone().take_while(|&x| x > 0).collect::<Vec<i32>>()
);
println!(
"{:?}",
iter.clone().take_until(|&x| x > 0).collect::<Vec<i32>>()
);
}
Output:
[1, 2, 3]
[1, 2, 3, -1]
Errors:
Compiling playground v0.0.1 (/playground)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.70s
Running `target/debug/playground`
However, I am still not happy about having to always have to program something so basic either, and feel like it should be there out of the box. Maybe something like this could be a candidate for an RFC? Or maybe I am missing something. Either way, if you have any thoughts or opinions about this I would love to hear them.