Skip to content

Commit 3fd91c4

Browse files
committed
Make overflow behaviour more obvious in the iterator module of libcore
Added documentation of the panicking overflow to the `count` and `position` methods of `IteratorExt`, also make them more obvious in the code. Also mention that the `Iterator::next` method of the struct returned by `IteratorExt::enumerate` can panic due to overflow.
1 parent b3317d6 commit 3fd91c4

File tree

1 file changed

+46
-10
lines changed

1 file changed

+46
-10
lines changed

src/libcore/iter.rs

+46-10
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ pub trait Iterator {
106106

107107
/// Counts the number of elements in this iterator.
108108
///
109+
/// # Panics
110+
///
111+
/// Panics if the number of elements overflows a `usize`.
112+
///
109113
/// # Examples
110114
///
111115
/// ```
@@ -115,7 +119,10 @@ pub trait Iterator {
115119
#[inline]
116120
#[stable(feature = "rust1", since = "1.0.0")]
117121
fn count(self) -> usize where Self: Sized {
118-
self.fold(0, |cnt, _x| cnt + 1)
122+
self.fold(0, |cnt, _| match cnt.checked_add(1) {
123+
Some(c) => c,
124+
None => panic!("overflow while counting the elements of an iterator"),
125+
})
119126
}
120127

121128
/// Loops through the entire iterator, returning the last element.
@@ -149,8 +156,10 @@ pub trait Iterator {
149156
#[stable(feature = "rust1", since = "1.0.0")]
150157
fn nth(&mut self, mut n: usize) -> Option<Self::Item> where Self: Sized {
151158
for x in self.by_ref() {
152-
if n == 0 { return Some(x) }
153-
n -= 1;
159+
n = match n.checked_sub(1) {
160+
Some(nn) => nn,
161+
None => return Some(x),
162+
}
154163
}
155164
None
156165
}
@@ -277,6 +286,11 @@ pub trait Iterator {
277286
/// `enumerate` keeps its count as a `usize`. If you want to count by a
278287
/// different sized integer, the `zip` function provides similar functionality.
279288
///
289+
/// # Panics
290+
///
291+
/// The returned iterator will panic if the to-be-returned index would
292+
/// overflow a `usize`.
293+
///
280294
/// # Examples
281295
///
282296
/// ```
@@ -289,7 +303,7 @@ pub trait Iterator {
289303
#[inline]
290304
#[stable(feature = "rust1", since = "1.0.0")]
291305
fn enumerate(self) -> Enumerate<Self> where Self: Sized {
292-
Enumerate{iter: self, count: 0}
306+
Enumerate { iter: self, count: Some(0) }
293307
}
294308

295309
/// Creates an iterator that has a `.peek()` method
@@ -665,6 +679,11 @@ pub trait Iterator {
665679
///
666680
/// Does not consume the iterator past the first found element.
667681
///
682+
/// # Panics
683+
///
684+
/// Panics if the number of elements overflows a `usize` and no matching
685+
/// element was found until that point.
686+
///
668687
/// # Examples
669688
///
670689
/// ```
@@ -684,7 +703,10 @@ pub trait Iterator {
684703
if predicate(x) {
685704
return Some(i);
686705
}
687-
i += 1;
706+
i = match i.checked_add(1) {
707+
Some(ii) => ii,
708+
None => panic!("overflow while getting the position of an element in an iterator"),
709+
}
688710
}
689711
None
690712
}
@@ -715,6 +737,8 @@ pub trait Iterator {
715737
if predicate(v) {
716738
return Some(i - 1);
717739
}
740+
// No need for an overflow check here, because `ExactSizeIterator`
741+
// implies that the number of elements fits into a `usize`.
718742
i -= 1;
719743
}
720744
None
@@ -1761,19 +1785,26 @@ impl<B, I: DoubleEndedIterator, F> DoubleEndedIterator for FilterMap<I, F>
17611785
#[stable(feature = "rust1", since = "1.0.0")]
17621786
pub struct Enumerate<I> {
17631787
iter: I,
1764-
count: usize
1788+
count: Option<usize>,
17651789
}
17661790

17671791
#[stable(feature = "rust1", since = "1.0.0")]
17681792
impl<I> Iterator for Enumerate<I> where I: Iterator {
17691793
type Item = (usize, <I as Iterator>::Item);
17701794

1795+
/// # Panics
1796+
///
1797+
/// Panics if the index of the element overflows a `usize`.
17711798
#[inline]
17721799
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
17731800
match self.iter.next() {
17741801
Some(a) => {
1775-
let ret = Some((self.count, a));
1776-
self.count += 1;
1802+
let count = match self.count {
1803+
Some(c) => c,
1804+
None => panic!("overflow while enumerating an iterator"),
1805+
};
1806+
let ret = Some((count, a));
1807+
self.count = count.checked_add(1);
17771808
ret
17781809
}
17791810
_ => None
@@ -1795,7 +1826,9 @@ impl<I> DoubleEndedIterator for Enumerate<I> where
17951826
match self.iter.next_back() {
17961827
Some(a) => {
17971828
let len = self.iter.len();
1798-
Some((self.count + len, a))
1829+
// Can safely `unwrap`, `ExactSizeIterator` promises that the
1830+
// number of elements fits into a `usize`.
1831+
Some((self.count.unwrap() + len, a))
17991832
}
18001833
_ => None
18011834
}
@@ -1812,7 +1845,10 @@ impl<I> RandomAccessIterator for Enumerate<I> where I: RandomAccessIterator {
18121845
#[inline]
18131846
fn idx(&mut self, index: usize) -> Option<(usize, <I as Iterator>::Item)> {
18141847
match self.iter.idx(index) {
1815-
Some(a) => Some((self.count + index, a)),
1848+
// Can safely `unwrap`, `ExactSizeIterator` (ancestor of
1849+
// `RandomAccessIterator`) promises that the number of elements
1850+
// fits into a `usize`.
1851+
Some(a) => Some((self.count.unwrap() + index, a)),
18161852
_ => None,
18171853
}
18181854
}

0 commit comments

Comments
 (0)