Missing `take_until` iterator

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>>()
    );
}

(Playground)

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.

APC, not RFC.

take_while_inclusive is another prior art.

Here's an existing issue about it.

You can see the itertools implementation here, which is pretty much how I would do it too. Nominal iterators are kinder for downstream and idiomatic for std.

1 Like

Thanks a lot for a very helpful answer! You are much better at googling than I am.

I am much happier having itertools for a dependency and am very happy there is an open issue for this already.