Skip to content

Add take_while_inclusive method to Iterator #142

Closed
@junbl

Description

@junbl

Proposal

Problem statement

The existing take_while method on Iterator is useful for getting elements that satisfy a predicate. However, it has the notable drawback of removing the first element that doesn't satisfy the predicate, since it must take that element from the iterator to determine if the predicate is finished.

This proposal adds a method take_while_inclusive that returns elements that satisfy a predicate, but also including that first element that didn't satisfy said predicate. The rest of the iterator is left unchanged and thus no elements are lost.

Motivation, use-cases

Here's the use case in which I wanted this method (paraphrasing since it's closed source). I was parsing a list of parameters that can contain in itself a list of values, so I needed to conditionally take more values from an iterator to create that element.

In this case, since the first element not satisfying the predicate represents a delimiter, it should be included with the other elements. In other cases, you might need to split that last element off to add it back into the collection.

const LIST_START: &str = "[";
const LIST_END: &str = "]";    
let s = "a,b,c,[d,e,f],g";
let mut items = s.split(',');
let mut collected = Vec::new();
while let Some(item) = items.next() {
    if item.starts_with(LIST_START) {
        // item is the start of a list, so collect all the remaining nested list elements together
        let list = std::iter::once(item)
            .chain((&mut items).take_while_inclusive(|s| !s.ends_with(LIST_END)))
            .collect();
        collected.push(list);
    } else {
        collected.push(item.to_string());
    }
}
assert_eq!(collected, ["a", "b", "c", "[def]", "g"].map(ToString::to_string));

Also see the first Rust issue in which this was discussed for an example of a use case involving histogram bins: rust-lang/rust#62208

Playground link to above

Solution sketches

In the absence of this method, I initially used the take_while_ref method from itertools. However, this clones the first element that doesn't satisfy the predicate, which is less than ideal in my use case and impossible in others. My current code uses peeking_take_while and manually adds that next element back.

Links and related work

My PR to itertools implementing said method: rust-itertools/itertools#616

First discussed addition to std in rust-lang/rust#62208

Issue for addition to itertools: rust-itertools/itertools#597

The PR to itertools has been put on hold before investigating if this is an appropriate addition to std (to prevent another instance of collision like intersperse).

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions