diff --git a/src/libcore/array.rs b/src/libcore/array.rs index 3d24f8902bd83..e9a0eec6657f9 100644 --- a/src/libcore/array.rs +++ b/src/libcore/array.rs @@ -198,6 +198,8 @@ macro_rules! array_impls { fn into_iter(self) -> Iter<'a, T> { self.iter() } + + const SIZE_HINT: (usize, Option) = ($N, Some($N)); } #[stable(feature = "rust1", since = "1.0.0")] @@ -208,6 +210,8 @@ macro_rules! array_impls { fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } + + const SIZE_HINT: (usize, Option) = ($N, Some($N)); } // NOTE: some less important impls are omitted to reduce code bloat diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 623cad754dd72..033847ee55528 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -2611,11 +2611,21 @@ impl Iterator for FlattenCompat fn size_hint(&self) -> (usize, Option) { let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()); - let lo = flo.saturating_add(blo); - match (self.iter.size_hint(), fhi, bhi) { - ((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)), - _ => (lo, None) - } + + let (ilo, ihi) = self.iter.size_hint(); + let (clo, chi) = ::SIZE_HINT; + let mlo = ilo.saturating_mul(clo); + let mhi = match (ihi, chi) { + (Some(0), _) => Some(0), + (Some(a), Some(b)) => a.checked_mul(b), + _ => None, + }; + + let lo = flo.saturating_add(blo).saturating_add(mlo); + let combine_hi = || { + fhi?.checked_add(bhi?)?.checked_add(mhi?) + }; + (lo, combine_hi()) } #[inline] diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 860742d9eab60..47c583ce5bc92 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -246,6 +246,28 @@ pub trait IntoIterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn into_iter(self) -> Self::IntoIter; + + /// A hint for the size of the iterators produced by this type. + /// + /// This allows things like `.flatten()` to return a meaningful + /// `.size_hint()` before the actual iterators have been produced. + /// + /// This is more useful than having it on `Iterator`, since finite + /// iterators always have a lower-bound size of `0` by definition. + /// A `&[T; N]`, in contrast, knows that its iterator will produce + /// exactly `N` items even though its iterator type is a normal + /// slice iterator that has a wide range of `.size_hint()`s. + /// + /// # Examples + /// + /// ``` + /// #![feature(typelevel_size_hint)] + /// + /// assert_eq!(<&'static [i32; 10] as IntoIterator>::SIZE_HINT, (10, Some(10))); + /// assert_eq!( as IntoIterator>::SIZE_HINT, (0, Some(1))); + /// ``` + #[unstable(feature = "typelevel_size_hint", issue = "7777777")] + const SIZE_HINT: (usize, Option) = (0, None); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/option.rs b/src/libcore/option.rs index b8fe28d0f0d71..ff8dd671656ea 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -953,6 +953,8 @@ impl IntoIterator for Option { fn into_iter(self) -> IntoIter { IntoIter { inner: Item { opt: self } } } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } #[stable(since = "1.4.0", feature = "option_iter")] @@ -963,6 +965,8 @@ impl<'a, T> IntoIterator for &'a Option { fn into_iter(self) -> Iter<'a, T> { self.iter() } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } #[stable(since = "1.4.0", feature = "option_iter")] @@ -973,6 +977,8 @@ impl<'a, T> IntoIterator for &'a mut Option { fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } #[stable(since = "1.12.0", feature = "option_from")] diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 3801db94e15d5..fb42ce9707607 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -977,6 +977,8 @@ impl IntoIterator for Result { fn into_iter(self) -> IntoIter { IntoIter { inner: self.ok() } } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } #[stable(since = "1.4.0", feature = "result_iter")] @@ -987,6 +989,8 @@ impl<'a, T, E> IntoIterator for &'a Result { fn into_iter(self) -> Iter<'a, T> { self.iter() } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } #[stable(since = "1.4.0", feature = "result_iter")] @@ -997,6 +1001,8 @@ impl<'a, T, E> IntoIterator for &'a mut Result { fn into_iter(self) -> IterMut<'a, T> { self.iter_mut() } + + const SIZE_HINT: (usize, Option) = (0, Some(1)); } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index edd75f7795ed7..00471f73fd04c 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -885,6 +885,23 @@ fn test_iterator_flatten() { i += 1; } assert_eq!(i, ys.len()); + + let aa = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; + let mut i = aa.iter().flatten(); + assert_eq!(i.size_hint(), (9, Some(9))); + // Ensure the frontiter and backiter are included + assert_eq!(i.next(), Some(&1)); + assert_eq!(i.size_hint(), (8, Some(8))); + assert_eq!(i.next_back(), Some(&9)); + assert_eq!(i.size_hint(), (7, Some(7))); + + let vv = vec![ vec![1] ]; + let mut i = vv.iter().flatten(); + // Vec precludes a good static estimate + assert_eq!(i.size_hint(), (0, None)); + i.next(); + // Until it's empty, where we no longer need the estimate + assert_eq!(i.size_hint(), (0, Some(0))); } /// Test `Flatten::fold` with items already picked off the front and back, @@ -1073,7 +1090,12 @@ fn test_iterator_size_hint() { assert_eq!(vi.clone().scan(0, |_,_| Some(0)).size_hint(), (0, Some(10))); assert_eq!(vi.clone().filter(|_| false).size_hint(), (0, Some(10))); assert_eq!(vi.clone().map(|&i| i+1).size_hint(), (10, Some(10))); + assert_eq!(vi.clone().filter_map(|_| Some(1)).size_hint(), (0, Some(10))); + assert_eq!(vi.clone().flat_map(|_| Some(1)).size_hint(), (0, Some(10))); assert_eq!(vi.filter_map(|_| Some(0)).size_hint(), (0, Some(10))); + + let vv = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; + assert_eq!(vv.iter().flatten().size_hint(), (9, Some(9))); } #[test]