I'm trying to create a peek_while
method for my iterator which should basically do the same as take_while
but only consume the character after the predicate matched. I've taken some inspiration from https://stackoverflow.com/a/30540952/10315665 and from the actual take_while
source code https://github.com/rust-lang/rust/blob/2c7bc5e33c25e29058cbafefe680da8d5e9220e9/library/core/src/iter/adapters/take_while.rs#L42-L54 and arrived at this result:
pub struct PeekWhile<I: Iterator, P> {
iter: Peekable<I>,
predicate: P,
}
impl<I, P> fmt::Debug for PeekWhile<I, P>
where
I: Iterator + Debug,
<I as Iterator>::Item: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PeekWhile")
.field("iter", &self.iter)
.finish()
}
}
impl<I, P> Iterator for PeekWhile<I, P>
where
I: Iterator,
P: FnMut(&I::Item) -> bool,
{
type Item = I::Item;
fn next(&mut self) -> Option<I::Item> {
let n = self.iter.peek()?;
while let Some(n) = self.iter.peek() {
if (self.predicate)(&n) {
return self.iter.next();
} else {
break;
}
}
None
}
}
pub trait PeekWhileExt: Iterator {
fn peek_while<P>(self, predicate: P) -> PeekWhile<Self, P>
where
Self: Iterator,
Self: Sized,
P: FnMut(&Self::Item) -> bool,
{
PeekWhile {
iter: self.peekable(),
predicate,
}
}
}
impl<I: Iterator> PeekWhileExt for I {}
This is causing an infinite loop, and while I'm not sure why, I see that the take_while::next
method does not have a loop, so I changed that to:
let n = self.iter.peek()?;
if (self.predicate)(n) {
Some(n)
} else {
None
}
which now gives me:
mismatched types
expected associated type `<I as Iterator>::Item`
found reference `&<I as Iterator>::Item`
So how would I go about creating such an iterator? Is the code so far correct, and how to I complete it? I know that itertools has https://docs.rs/itertools/0.10.1/itertools/trait.Itertools.html#method.peeking_take_while which sounds promising, but this is a learning project, both in programming concepts and rust itself (I'm creating a JSON parser btw), so I would very much be interested in completing that portion of the code without any libraries.
Example use case (not tested):
let chars = "keyword:".chars();
assert_eq!(chars.peek_while(|c| c.is_alphabetic()).collect::<String>(), "keyword");
assert_eq!(chars.next().unwrap(), ':');
// ^
// Very important that the next char doesn't get lost
Appreciate any help!