-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add Iterator::map_while
#66577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Iterator::map_while
#66577
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1752,6 +1752,95 @@ where | |
} | ||
} | ||
|
||
/// An iterator that only accepts elements while `predicate` returns `Some(_)`. | ||
/// | ||
/// This `struct` is created by the [`map_while`] method on [`Iterator`]. See its | ||
/// documentation for more. | ||
/// | ||
/// [`map_while`]: trait.Iterator.html#method.map_while | ||
/// [`Iterator`]: trait.Iterator.html | ||
#[must_use = "iterators are lazy and do nothing unless consumed"] | ||
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] | ||
#[derive(Clone)] | ||
pub struct MapWhile<I, P> { | ||
iter: I, | ||
finished: bool, | ||
predicate: P, | ||
} | ||
|
||
impl<I, P> MapWhile<I, P> { | ||
pub(super) fn new(iter: I, predicate: P) -> MapWhile<I, P> { | ||
MapWhile { iter, finished: false, predicate } | ||
} | ||
} | ||
|
||
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] | ||
impl<I: fmt::Debug, P> fmt::Debug for MapWhile<I, P> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("MapWhile").field("iter", &self.iter).field("flag", &self.finished).finish() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to expose a flag in debug output? Either the way, the name should be corrected to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if it makes sense, but it's exposed in |
||
} | ||
} | ||
|
||
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] | ||
impl<B, I: Iterator, P> Iterator for MapWhile<I, P> | ||
WaffleLapkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
where | ||
P: FnMut(I::Item) -> Option<B>, | ||
{ | ||
type Item = B; | ||
|
||
#[inline] | ||
fn next(&mut self) -> Option<B> { | ||
if self.finished { | ||
None | ||
} else { | ||
let x = self.iter.next()?; | ||
let ret = (self.predicate)(x); | ||
self.finished = ret.is_none(); | ||
ret | ||
} | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> (usize, Option<usize>) { | ||
if self.finished { | ||
(0, Some(0)) | ||
} else { | ||
let (_, upper) = self.iter.size_hint(); | ||
(0, upper) // can't know a lower bound, due to the predicate | ||
} | ||
} | ||
|
||
#[inline] | ||
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R | ||
where | ||
Self: Sized, | ||
Fold: FnMut(Acc, Self::Item) -> R, | ||
R: Try<Ok = Acc>, | ||
{ | ||
fn check<'a, B, T, Acc, R: Try<Ok = Acc>>( | ||
flag: &'a mut bool, | ||
p: &'a mut impl FnMut(T) -> Option<B>, | ||
mut fold: impl FnMut(Acc, B) -> R + 'a, | ||
) -> impl FnMut(Acc, T) -> LoopState<Acc, R> + 'a { | ||
move |acc, x| match p(x) { | ||
Some(item) => LoopState::from_try(fold(acc, item)), | ||
None => { | ||
*flag = true; | ||
LoopState::Break(Try::from_ok(acc)) | ||
} | ||
} | ||
} | ||
|
||
if self.finished { | ||
Try::from_ok(init) | ||
} else { | ||
let flag = &mut self.finished; | ||
let p = &mut self.predicate; | ||
self.iter.try_fold(init, check(flag, p, fold)).into_try() | ||
} | ||
} | ||
} | ||
|
||
#[stable(feature = "fused", since = "1.26.0")] | ||
impl<I, P> FusedIterator for TakeWhile<I, P> | ||
WaffleLapkin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
where | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the
finished
field needed? It looks like it's only used to makeMapWhile
fused but that seems unnecessary when you can instead just call.fuse()
if needed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whole point of the
MapWhile
is that it stops on firstNone
just likeTakeWhile
stops on firstfalse
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It stops by having
.next()
returnNone
. Thefinished
field isn't needed to do that. After then it doesn't matter what.next()
returns unless the iterator implementsFusedIterator
whichMapWhile
doesn't.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I first thought it was to improve the upper bound of
size_hint
, but it also doesn't matter what that method returns once the iterator has been exhausted. So I suppose you're right. We probably can't remove it fromTakeWhile
though, since it already implementsFusedIterator
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be a bit confusing, so if we do remove
finished
flag, then I think we need to mention it in the docs