Skip to content

Commit db2a86b

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 5dc8060 commit db2a86b

File tree

1 file changed

+50
-6
lines changed

1 file changed

+50
-6
lines changed

src/libcore/iter.rs

+50-6
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
}
@@ -280,6 +289,16 @@ pub trait Iterator {
280289
/// different sized integer, the `zip` function provides similar
281290
/// functionality.
282291
///
292+
/// # Undefined overflow
293+
///
294+
/// The method does no guarding against overflows, so enumerating more than
295+
/// `usize::MAX` elements is undefined.
296+
///
297+
/// # Panics
298+
///
299+
/// The returned iterator might panic if the to-be-returned index would
300+
/// overflow a `usize`.
301+
///
283302
/// # Examples
284303
///
285304
/// ```
@@ -292,7 +311,7 @@ pub trait Iterator {
292311
#[inline]
293312
#[stable(feature = "rust1", since = "1.0.0")]
294313
fn enumerate(self) -> Enumerate<Self> where Self: Sized {
295-
Enumerate{iter: self, count: 0}
314+
Enumerate { iter: self, count: 0 }
296315
}
297316

298317
/// Creates an iterator that has a `.peek()` method
@@ -674,6 +693,11 @@ pub trait Iterator {
674693
///
675694
/// Does not consume the iterator past the first found element.
676695
///
696+
/// # Panics
697+
///
698+
/// Panics if the number of elements overflows a `usize` and no matching
699+
/// element was found until that point.
700+
///
677701
/// # Examples
678702
///
679703
/// ```
@@ -693,7 +717,10 @@ pub trait Iterator {
693717
if predicate(x) {
694718
return Some(i);
695719
}
696-
i += 1;
720+
i = match i.checked_add(1) {
721+
Some(ii) => ii,
722+
None => panic!("overflow while getting the position of an element in an iterator"),
723+
}
697724
}
698725
None
699726
}
@@ -724,6 +751,8 @@ pub trait Iterator {
724751
if predicate(v) {
725752
return Some(i - 1);
726753
}
754+
// No need for an overflow check here, because `ExactSizeIterator`
755+
// implies that the number of elements fits into a `usize`.
727756
i -= 1;
728757
}
729758
None
@@ -1761,17 +1790,27 @@ impl<B, I: DoubleEndedIterator, F> DoubleEndedIterator for FilterMap<I, F>
17611790
#[stable(feature = "rust1", since = "1.0.0")]
17621791
pub struct Enumerate<I> {
17631792
iter: I,
1764-
count: usize
1793+
count: usize,
17651794
}
17661795

17671796
#[stable(feature = "rust1", since = "1.0.0")]
17681797
impl<I> Iterator for Enumerate<I> where I: Iterator {
17691798
type Item = (usize, <I as Iterator>::Item);
17701799

1800+
1801+
/// # Undefined overflow
1802+
///
1803+
/// The method does no guarding against overflows, so enumerating more than
1804+
/// `usize::MAX` elements is undefined.
1805+
///
1806+
/// # Panics
1807+
///
1808+
/// Might panic if the index of the element overflows a `usize`.
17711809
#[inline]
17721810
fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
17731811
self.iter.next().map(|a| {
17741812
let ret = (self.count, a);
1813+
// Possible undefined overflow.
17751814
self.count += 1;
17761815
ret
17771816
})
@@ -1791,6 +1830,8 @@ impl<I> DoubleEndedIterator for Enumerate<I> where
17911830
fn next_back(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
17921831
self.iter.next_back().map(|a| {
17931832
let len = self.iter.len();
1833+
// Can safely add, `ExactSizeIterator` promises that the number of
1834+
// elements fits into a `usize`.
17941835
(self.count + len, a)
17951836
})
17961837
}
@@ -1805,6 +1846,9 @@ impl<I> RandomAccessIterator for Enumerate<I> where I: RandomAccessIterator {
18051846

18061847
#[inline]
18071848
fn idx(&mut self, index: usize) -> Option<(usize, <I as Iterator>::Item)> {
1849+
// Can safely add, `ExactSizeIterator` (ancestor of
1850+
// `RandomAccessIterator`) promises that the number of elements fits
1851+
// into a `usize`.
18081852
self.iter.idx(index).map(|a| (self.count + index, a))
18091853
}
18101854
}

0 commit comments

Comments
 (0)