diff --git a/src/libcore/iter/adapters/chain.rs b/src/libcore/iter/adapters/chain.rs index 3b669cad1c40f..1c0532632b00b 100644 --- a/src/libcore/iter/adapters/chain.rs +++ b/src/libcore/iter/adapters/chain.rs @@ -283,6 +283,36 @@ impl<A, B> FusedIterator for Chain<A, B> B: FusedIterator<Item=A::Item>, {} +#[stable(feature = "chain_exact_size", since = "1.41.0")] +impl<A, B> ExactSizeIterator for Chain<A, B> +where + A: ExactSizeIterator, + B: ExactSizeIterator<Item = A::Item>, +{ + /// Returns the exact number of times the iterator will iterate. + /// + /// # Overflow Behavior + /// + /// Calling this method on an iterator with more than [`usize::MAX`] + /// elements will result in a panic. + /// + /// # Panics + /// + /// This panics if the iterator has more than [`usize::MAX`] elements. + /// + /// [`usize::MAX`]: ../../std/usize/constant.MAX.html + fn len(&self) -> usize { + match self.state { + ChainState::Both => { + self.a.len().checked_add(self.b.len()) + .expect("called `len` on iterator with more than `usize::MAX` elements") + } + ChainState::Front => self.a.len(), + ChainState::Back => self.b.len(), + } + } +} + #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl<A, B> TrustedLen for Chain<A, B> where A: TrustedLen, B: TrustedLen<Item=A::Item>, diff --git a/src/libcore/iter/traits/iterator.rs b/src/libcore/iter/traits/iterator.rs index b7a35568e3fc5..17f6ea1177ed0 100644 --- a/src/libcore/iter/traits/iterator.rs +++ b/src/libcore/iter/traits/iterator.rs @@ -1,3 +1,7 @@ +// ignore-tidy-filelength +// This file almost exclusively consists of the definition of `Iterator`. We +// can't split that into multiple files. + use crate::cmp::{self, Ordering}; use crate::ops::{Add, Try}; @@ -388,6 +392,26 @@ pub trait Iterator { /// [`once`] is commonly used to adapt a single value into a chain of /// other kinds of iteration. /// + /// + /// # Overflowing behavior for long iterators + /// + /// This method allows to easily build an iterator that yields more than + /// [`usize::MAX`] items. In that case, some methods that return `usize` + /// are not guarded against overflow. For example, this includes the + /// following methods: + /// + /// - [`Iterator::count`] + /// - [`Iterator::enumerate`] + /// - [`Iterator::position`] and [`Iterator::rposition`] + /// - [`ExactSizeIterator::len`] + /// + /// An overflow in those methods leads to a wrong result or a panic. If + /// debug assertions are enabled, a panic is guaranteed. + /// + /// + /// [`usize::MAX`]: ../../std/usize/constant.MAX.html + /// + /// /// # Examples /// /// Basic usage: diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index c9096b713f20e..7c3ce7b838906 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -256,6 +256,50 @@ fn test_iterator_chain_size_hint() { assert_eq!(iter.size_hint(), (0, Some(0))); } +#[test] +fn test_iterator_chain_len() { + let xs = [0, 1, 2]; + let ys = [30, 40, 50, 60]; + + // First iterator is exhausted first + let mut iter = xs.iter().chain(&ys); + assert_eq!(iter.len(), 7); + assert_eq!(iter.next(), Some(&0)); + assert_eq!(iter.len(), 6); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.len(), 5); + assert_eq!(iter.next_back(), Some(&60)); + assert_eq!(iter.len(), 4); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.len(), 3); + assert_eq!(iter.next(), Some(&30)); + assert_eq!(iter.len(), 2); + assert_eq!(iter.next(), Some(&40)); + assert_eq!(iter.len(), 1); + assert_eq!(iter.next(), Some(&50)); + assert_eq!(iter.len(), 0); + assert_eq!(iter.next(), None); + + // Second iterator is exhausted first + let mut iter = xs.iter().chain(&ys); + assert_eq!(iter.len(), 7); + assert_eq!(iter.next_back(), Some(&60)); + assert_eq!(iter.len(), 6); + assert_eq!(iter.next(), Some(&0)); + assert_eq!(iter.len(), 5); + assert_eq!(iter.next_back(), Some(&50)); + assert_eq!(iter.len(), 4); + assert_eq!(iter.next_back(), Some(&40)); + assert_eq!(iter.len(), 3); + assert_eq!(iter.next_back(), Some(&30)); + assert_eq!(iter.len(), 2); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.len(), 1); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.len(), 0); + assert_eq!(iter.next(), None); +} + #[test] fn test_zip_nth() { let xs = [0, 1, 2, 4, 5];