Skip to content

Commit 34700c1

Browse files
committed
Auto merge of #66577 - WaffleLapkin:iter_take_while_map, r=mark-simulcrum
Add `Iterator::map_while` In `Iterator` trait there is `*_map` version of [`filter`] — [`filter_map`], however, there is no `*_map` version of [`take_while`], that can also be useful. ### Use cases In my code, I've found that I need to iterate through iterator of `Option`s, stopping on the first `None`. So I've written code like this: ```rust let arr = [Some(4), Some(10), None, Some(3)]; let mut iter = arr.iter() .take_while(|x| x.is_some()) .map(|x| x.unwrap()); assert_eq!(iter.next(), Some(4)); assert_eq!(iter.next(), Some(10)); assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); ``` Thit code 1) isn't clean 2) In theory, can generate bad bytecode (I'm actually **not** sure, but I think that `unwrap` would generate additional branches with `panic!`) The same code, but with `map_while` (in the original PR message it was named "take_while_map"): ```rust let arr = [Some(4), Some(10), None, Some(3)]; let mut iter = arr.iter().map_while(std::convert::identity); ``` Also, `map_while` can be useful when converting something (as in [examples]). [`filter`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter [`filter_map`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map [`take_while`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take_while [examples]: https://github.com/rust-lang/rust/compare/master...WaffleLapkin:iter_take_while_map?expand=1#diff-7e57917f962fe6ffdfba51e4955ad6acR1042
2 parents 212b2c7 + db1a107 commit 34700c1

File tree

5 files changed

+195
-1
lines changed

5 files changed

+195
-1
lines changed

src/libcore/iter/adapters/mod.rs

+89
Original file line numberDiff line numberDiff line change
@@ -1752,6 +1752,95 @@ where
17521752
}
17531753
}
17541754

1755+
/// An iterator that only accepts elements while `predicate` returns `Some(_)`.
1756+
///
1757+
/// This `struct` is created by the [`map_while`] method on [`Iterator`]. See its
1758+
/// documentation for more.
1759+
///
1760+
/// [`map_while`]: trait.Iterator.html#method.map_while
1761+
/// [`Iterator`]: trait.Iterator.html
1762+
#[must_use = "iterators are lazy and do nothing unless consumed"]
1763+
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
1764+
#[derive(Clone)]
1765+
pub struct MapWhile<I, P> {
1766+
iter: I,
1767+
finished: bool,
1768+
predicate: P,
1769+
}
1770+
1771+
impl<I, P> MapWhile<I, P> {
1772+
pub(super) fn new(iter: I, predicate: P) -> MapWhile<I, P> {
1773+
MapWhile { iter, finished: false, predicate }
1774+
}
1775+
}
1776+
1777+
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
1778+
impl<I: fmt::Debug, P> fmt::Debug for MapWhile<I, P> {
1779+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1780+
f.debug_struct("MapWhile").field("iter", &self.iter).field("flag", &self.finished).finish()
1781+
}
1782+
}
1783+
1784+
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
1785+
impl<B, I: Iterator, P> Iterator for MapWhile<I, P>
1786+
where
1787+
P: FnMut(I::Item) -> Option<B>,
1788+
{
1789+
type Item = B;
1790+
1791+
#[inline]
1792+
fn next(&mut self) -> Option<B> {
1793+
if self.finished {
1794+
None
1795+
} else {
1796+
let x = self.iter.next()?;
1797+
let ret = (self.predicate)(x);
1798+
self.finished = ret.is_none();
1799+
ret
1800+
}
1801+
}
1802+
1803+
#[inline]
1804+
fn size_hint(&self) -> (usize, Option<usize>) {
1805+
if self.finished {
1806+
(0, Some(0))
1807+
} else {
1808+
let (_, upper) = self.iter.size_hint();
1809+
(0, upper) // can't know a lower bound, due to the predicate
1810+
}
1811+
}
1812+
1813+
#[inline]
1814+
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
1815+
where
1816+
Self: Sized,
1817+
Fold: FnMut(Acc, Self::Item) -> R,
1818+
R: Try<Ok = Acc>,
1819+
{
1820+
fn check<'a, B, T, Acc, R: Try<Ok = Acc>>(
1821+
flag: &'a mut bool,
1822+
p: &'a mut impl FnMut(T) -> Option<B>,
1823+
mut fold: impl FnMut(Acc, B) -> R + 'a,
1824+
) -> impl FnMut(Acc, T) -> LoopState<Acc, R> + 'a {
1825+
move |acc, x| match p(x) {
1826+
Some(item) => LoopState::from_try(fold(acc, item)),
1827+
None => {
1828+
*flag = true;
1829+
LoopState::Break(Try::from_ok(acc))
1830+
}
1831+
}
1832+
}
1833+
1834+
if self.finished {
1835+
Try::from_ok(init)
1836+
} else {
1837+
let flag = &mut self.finished;
1838+
let p = &mut self.predicate;
1839+
self.iter.try_fold(init, check(flag, p, fold)).into_try()
1840+
}
1841+
}
1842+
}
1843+
17551844
#[stable(feature = "fused", since = "1.26.0")]
17561845
impl<I, P> FusedIterator for TakeWhile<I, P>
17571846
where

src/libcore/iter/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ pub use self::adapters::Cloned;
351351
pub use self::adapters::Copied;
352352
#[stable(feature = "iterator_flatten", since = "1.29.0")]
353353
pub use self::adapters::Flatten;
354+
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
355+
pub use self::adapters::MapWhile;
354356
#[stable(feature = "iterator_step_by", since = "1.28.0")]
355357
pub use self::adapters::StepBy;
356358
#[stable(feature = "rust1", since = "1.0.0")]

src/libcore/iter/traits/iterator.rs

+101-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// ignore-tidy-filelength
2+
// This file almost exclusively consists of the definition of `Iterator`. We
3+
// can't split that into multiple files.
24

35
use crate::cmp::{self, Ordering};
46
use crate::ops::{Add, Try};
@@ -7,7 +9,9 @@ use super::super::LoopState;
79
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
810
use super::super::{FlatMap, Flatten};
911
use super::super::{FromIterator, Product, Sum, Zip};
10-
use super::super::{Inspect, Map, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile};
12+
use super::super::{
13+
Inspect, Map, MapWhile, Peekable, Rev, Scan, Skip, SkipWhile, StepBy, Take, TakeWhile,
14+
};
1115

1216
fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
1317

@@ -1026,6 +1030,102 @@ pub trait Iterator {
10261030
TakeWhile::new(self, predicate)
10271031
}
10281032

1033+
/// Creates an iterator that both yields elements based on a predicate and maps.
1034+
///
1035+
/// `map_while()` takes a closure as an argument. It will call this
1036+
/// closure on each element of the iterator, and yield elements
1037+
/// while it returns [`Some(_)`][`Some`].
1038+
///
1039+
/// After [`None`] is returned, `map_while()`'s job is over, and the
1040+
/// rest of the elements are ignored.
1041+
///
1042+
/// # Examples
1043+
///
1044+
/// Basic usage:
1045+
///
1046+
/// ```
1047+
/// #![feature(iter_map_while)]
1048+
/// let a = [-1i32, 4, 0, 1];
1049+
///
1050+
/// let mut iter = a.iter().map_while(|x| 16i32.checked_div(*x));
1051+
///
1052+
/// assert_eq!(iter.next(), Some(-16));
1053+
/// assert_eq!(iter.next(), Some(4));
1054+
/// assert_eq!(iter.next(), None);
1055+
/// ```
1056+
///
1057+
/// Here's the same example, but with [`take_while`] and [`map`]:
1058+
///
1059+
/// [`take_while`]: #method.take_while
1060+
/// [`map`]: #method.map
1061+
///
1062+
/// ```
1063+
/// let a = [-1i32, 4, 0, 1];
1064+
///
1065+
/// let mut iter = a.iter()
1066+
/// .map(|x| 16i32.checked_div(*x))
1067+
/// .take_while(|x| x.is_some())
1068+
/// .map(|x| x.unwrap());
1069+
///
1070+
/// assert_eq!(iter.next(), Some(-16));
1071+
/// assert_eq!(iter.next(), Some(4));
1072+
/// assert_eq!(iter.next(), None);
1073+
/// ```
1074+
///
1075+
/// Stopping after an initial [`None`]:
1076+
///
1077+
/// ```
1078+
/// #![feature(iter_map_while)]
1079+
/// use std::convert::TryFrom;
1080+
///
1081+
/// let a = [0, -1, 1, -2];
1082+
///
1083+
/// let mut iter = a.iter().map_while(|x| u32::try_from(*x).ok());
1084+
///
1085+
/// assert_eq!(iter.next(), Some(0u32));
1086+
///
1087+
/// // We have more elements that are fit in u32, but since we already
1088+
/// // got a None, map_while() isn't used any more
1089+
/// assert_eq!(iter.next(), None);
1090+
/// ```
1091+
///
1092+
/// Because `map_while()` needs to look at the value in order to see if it
1093+
/// should be included or not, consuming iterators will see that it is
1094+
/// removed:
1095+
///
1096+
/// ```
1097+
/// #![feature(iter_map_while)]
1098+
/// use std::convert::TryFrom;
1099+
///
1100+
/// let a = [1, 2, -3, 4];
1101+
/// let mut iter = a.iter();
1102+
///
1103+
/// let result: Vec<u32> = iter.by_ref()
1104+
/// .map_while(|n| u32::try_from(*n).ok())
1105+
/// .collect();
1106+
///
1107+
/// assert_eq!(result, &[1, 2]);
1108+
///
1109+
/// let result: Vec<i32> = iter.cloned().collect();
1110+
///
1111+
/// assert_eq!(result, &[4]);
1112+
/// ```
1113+
///
1114+
/// The `-3` is no longer there, because it was consumed in order to see if
1115+
/// the iteration should stop, but wasn't placed back into the iterator.
1116+
///
1117+
/// [`Some`]: ../../std/option/enum.Option.html#variant.Some
1118+
/// [`None`]: ../../std/option/enum.Option.html#variant.None
1119+
#[inline]
1120+
#[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")]
1121+
fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
1122+
where
1123+
Self: Sized,
1124+
P: FnMut(Self::Item) -> Option<B>,
1125+
{
1126+
MapWhile::new(self, predicate)
1127+
}
1128+
10291129
/// Creates an iterator that skips the first `n` elements.
10301130
///
10311131
/// After they have been consumed, the rest of the elements are yielded.

src/libcore/tests/iter.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,7 @@ fn test_iterator_size_hint() {
14771477
assert_eq!(c.clone().take(5).size_hint(), (5, Some(5)));
14781478
assert_eq!(c.clone().skip(5).size_hint().1, None);
14791479
assert_eq!(c.clone().take_while(|_| false).size_hint(), (0, None));
1480+
assert_eq!(c.clone().map_while(|_| None::<()>).size_hint(), (0, None));
14801481
assert_eq!(c.clone().skip_while(|_| false).size_hint(), (0, None));
14811482
assert_eq!(c.clone().enumerate().size_hint(), (usize::MAX, None));
14821483
assert_eq!(c.clone().chain(vi.clone().cloned()).size_hint(), (usize::MAX, None));
@@ -1491,6 +1492,7 @@ fn test_iterator_size_hint() {
14911492
assert_eq!(vi.clone().skip(3).size_hint(), (7, Some(7)));
14921493
assert_eq!(vi.clone().skip(12).size_hint(), (0, Some(0)));
14931494
assert_eq!(vi.clone().take_while(|_| false).size_hint(), (0, Some(10)));
1495+
assert_eq!(vi.clone().map_while(|_| None::<()>).size_hint(), (0, Some(10)));
14941496
assert_eq!(vi.clone().skip_while(|_| false).size_hint(), (0, Some(10)));
14951497
assert_eq!(vi.clone().enumerate().size_hint(), (10, Some(10)));
14961498
assert_eq!(vi.clone().chain(v2).size_hint(), (13, Some(13)));

src/libcore/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#![feature(iter_is_partitioned)]
3737
#![feature(iter_order_by)]
3838
#![feature(cmp_min_max_by)]
39+
#![feature(iter_map_while)]
3940
#![feature(const_slice_from_raw_parts)]
4041
#![feature(const_raw_ptr_deref)]
4142
#![feature(never_type)]

0 commit comments

Comments
 (0)