Skip to content

Commit 6c2a456

Browse files
committed
core::iter: Peekable should remember peeking a None
Peekable must remember if a None has been seen in the `.peek()` method. It ensures that `.peek(); .peek();` or `.peek(); .next();` only advances the underlying iterator at most once. This does not by itself make the iterator fused.
1 parent 0ed9519 commit 6c2a456

File tree

2 files changed

+109
-23
lines changed

2 files changed

+109
-23
lines changed

src/libcore/iter/mod.rs

+41-23
Original file line numberDiff line numberDiff line change
@@ -1273,54 +1273,68 @@ unsafe impl<I> TrustedLen for Enumerate<I>
12731273
#[stable(feature = "rust1", since = "1.0.0")]
12741274
pub struct Peekable<I: Iterator> {
12751275
iter: I,
1276-
peeked: Option<I::Item>,
1276+
/// Remember a peeked value, even if it was None.
1277+
peeked: Option<Option<I::Item>>,
12771278
}
12781279

1280+
// Peekable must remember if a None has been seen in the `.peek()` method.
1281+
// It ensures that `.peek(); .peek();` or `.peek(); .next();` only advances the
1282+
// underlying iterator at most once. This does not by itself make the iterator
1283+
// fused.
12791284
#[stable(feature = "rust1", since = "1.0.0")]
12801285
impl<I: Iterator> Iterator for Peekable<I> {
12811286
type Item = I::Item;
12821287

12831288
#[inline]
12841289
fn next(&mut self) -> Option<I::Item> {
1285-
match self.peeked {
1286-
Some(_) => self.peeked.take(),
1290+
match self.peeked.take() {
1291+
Some(v) => v,
12871292
None => self.iter.next(),
12881293
}
12891294
}
12901295

12911296
#[inline]
12921297
#[rustc_inherit_overflow_checks]
1293-
fn count(self) -> usize {
1294-
(if self.peeked.is_some() { 1 } else { 0 }) + self.iter.count()
1298+
fn count(mut self) -> usize {
1299+
match self.peeked.take() {
1300+
Some(None) => 0,
1301+
Some(Some(_)) => 1 + self.iter.count(),
1302+
None => self.iter.count(),
1303+
}
12951304
}
12961305

12971306
#[inline]
12981307
fn nth(&mut self, n: usize) -> Option<I::Item> {
1299-
match self.peeked {
1300-
Some(_) if n == 0 => self.peeked.take(),
1301-
Some(_) => {
1302-
self.peeked = None;
1303-
self.iter.nth(n-1)
1304-
},
1305-
None => self.iter.nth(n)
1308+
match self.peeked.take() {
1309+
// the .take() below is just to avoid "move into pattern guard"
1310+
Some(ref mut v) if n == 0 => v.take(),
1311+
Some(None) => None,
1312+
Some(Some(_)) => self.iter.nth(n - 1),
1313+
None => self.iter.nth(n),
13061314
}
13071315
}
13081316

13091317
#[inline]
1310-
fn last(self) -> Option<I::Item> {
1311-
self.iter.last().or(self.peeked)
1318+
fn last(mut self) -> Option<I::Item> {
1319+
let peek_opt = match self.peeked.take() {
1320+
Some(None) => return None,
1321+
Some(v) => v,
1322+
None => None,
1323+
};
1324+
self.iter.last().or(peek_opt)
13121325
}
13131326

13141327
#[inline]
13151328
fn size_hint(&self) -> (usize, Option<usize>) {
1329+
let peek_len = match self.peeked {
1330+
Some(None) => return (0, Some(0)),
1331+
Some(Some(_)) => 1,
1332+
None => 0,
1333+
};
13161334
let (lo, hi) = self.iter.size_hint();
1317-
if self.peeked.is_some() {
1318-
let lo = lo.saturating_add(1);
1319-
let hi = hi.and_then(|x| x.checked_add(1));
1320-
(lo, hi)
1321-
} else {
1322-
(lo, hi)
1323-
}
1335+
let lo = lo.saturating_add(peek_len);
1336+
let hi = hi.and_then(|x| x.checked_add(peek_len));
1337+
(lo, hi)
13241338
}
13251339
}
13261340

@@ -1372,9 +1386,13 @@ impl<I: Iterator> Peekable<I> {
13721386
#[stable(feature = "rust1", since = "1.0.0")]
13731387
pub fn peek(&mut self) -> Option<&I::Item> {
13741388
if self.peeked.is_none() {
1375-
self.peeked = self.iter.next();
1389+
self.peeked = Some(self.iter.next());
1390+
}
1391+
match self.peeked {
1392+
Some(Some(ref value)) => Some(value),
1393+
Some(None) => None,
1394+
_ => unreachable!(),
13761395
}
1377-
self.peeked.as_ref()
13781396
}
13791397
}
13801398

src/libcoretest/iter.rs

+68
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,74 @@ fn test_iterator_peekable_last() {
274274
let mut it = ys.iter().peekable();
275275
assert_eq!(it.peek(), Some(&&0));
276276
assert_eq!(it.last(), Some(&0));
277+
278+
let mut it = ys.iter().peekable();
279+
assert_eq!(it.next(), Some(&0));
280+
assert_eq!(it.peek(), None);
281+
assert_eq!(it.last(), None);
282+
}
283+
284+
/// This is an iterator that follows the Iterator contract,
285+
/// but it is not fused. After having returned None once, it will start
286+
/// producing elements if .next() is called again.
287+
pub struct CycleIter<'a, T: 'a> {
288+
index: usize,
289+
data: &'a [T],
290+
}
291+
292+
pub fn cycle<T>(data: &[T]) -> CycleIter<T> {
293+
CycleIter {
294+
index: 0,
295+
data: data,
296+
}
297+
}
298+
299+
impl<'a, T> Iterator for CycleIter<'a, T> {
300+
type Item = &'a T;
301+
fn next(&mut self) -> Option<Self::Item> {
302+
let elt = self.data.get(self.index);
303+
self.index += 1;
304+
self.index %= 1 + self.data.len();
305+
elt
306+
}
307+
}
308+
309+
#[test]
310+
fn test_iterator_peekable_remember_peek_none_1() {
311+
// Check that the loop using .peek() terminates
312+
let data = [1, 2, 3];
313+
let mut iter = cycle(&data).peekable();
314+
315+
let mut n = 0;
316+
while let Some(_) = iter.next() {
317+
let is_the_last = iter.peek().is_none();
318+
assert_eq!(is_the_last, n == data.len() - 1);
319+
n += 1;
320+
if n > data.len() { break; }
321+
}
322+
assert_eq!(n, data.len());
323+
}
324+
325+
#[test]
326+
fn test_iterator_peekable_remember_peek_none_2() {
327+
let data = [0];
328+
let mut iter = cycle(&data).peekable();
329+
iter.next();
330+
assert_eq!(iter.peek(), None);
331+
assert_eq!(iter.last(), None);
332+
}
333+
334+
#[test]
335+
fn test_iterator_peekable_remember_peek_none_3() {
336+
let data = [0];
337+
let mut iter = cycle(&data).peekable();
338+
iter.peek();
339+
assert_eq!(iter.nth(0), Some(&0));
340+
341+
let mut iter = cycle(&data).peekable();
342+
iter.next();
343+
assert_eq!(iter.peek(), None);
344+
assert_eq!(iter.nth(0), None);
277345
}
278346

279347
#[test]

0 commit comments

Comments
 (0)