Skip to content

Commit fc01f20

Browse files
committed
Implement size_hint for Range
Closes #8606
1 parent 2c18983 commit fc01f20

File tree

1 file changed

+80
-8
lines changed

1 file changed

+80
-8
lines changed

src/libstd/iter.rs

+80-8
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ the rest of the rust manuals.
6565
*/
6666

6767
use cmp;
68-
use num::{Zero, One, Integer, CheckedAdd, CheckedSub, Saturating};
68+
use num::{Zero, One, Integer, CheckedAdd, CheckedSub, Saturating, ToPrimitive};
6969
use option::{Option, Some, None};
7070
use ops::{Add, Mul, Sub};
7171
use cmp::{Eq, Ord};
@@ -1829,7 +1829,8 @@ pub fn range<A: Add<A, A> + Ord + Clone + One>(start: A, stop: A) -> Range<A> {
18291829
Range{state: start, stop: stop, one: One::one()}
18301830
}
18311831

1832-
impl<A: Add<A, A> + Ord + Clone> Iterator<A> for Range<A> {
1832+
// FIXME: #10414: Unfortunate type bound
1833+
impl<A: Add<A, A> + Ord + Clone + ToPrimitive> Iterator<A> for Range<A> {
18331834
#[inline]
18341835
fn next(&mut self) -> Option<A> {
18351836
if self.state < self.stop {
@@ -1841,13 +1842,42 @@ impl<A: Add<A, A> + Ord + Clone> Iterator<A> for Range<A> {
18411842
}
18421843
}
18431844

1844-
// FIXME: #8606 Implement size_hint() on Range
1845-
// Blocked on #8605 Need numeric trait for converting to `Option<uint>`
1845+
#[inline]
1846+
fn size_hint(&self) -> (uint, Option<uint>) {
1847+
// This first checks if the elements are representable as i64. If they aren't, try u64 (to
1848+
// handle cases like range(huge, huger)). We don't use uint/int because the difference of
1849+
// the i64/u64 might lie within their range.
1850+
let bound = match self.state.to_i64() {
1851+
Some(a) => {
1852+
let sz = self.stop.to_i64().map(|b| b.checked_sub(&a));
1853+
match sz {
1854+
Some(Some(bound)) => bound.to_uint(),
1855+
_ => None,
1856+
}
1857+
},
1858+
None => match self.state.to_u64() {
1859+
Some(a) => {
1860+
let sz = self.stop.to_u64().map(|b| b.checked_sub(&a));
1861+
match sz {
1862+
Some(Some(bound)) => bound.to_uint(),
1863+
_ => None
1864+
}
1865+
},
1866+
None => None
1867+
}
1868+
};
1869+
1870+
match bound {
1871+
Some(b) => (b, Some(b)),
1872+
// Standard fallback for unbounded/unrepresentable bounds
1873+
None => (0, None)
1874+
}
1875+
}
18461876
}
18471877

18481878
/// `Integer` is required to ensure the range will be the same regardless of
18491879
/// the direction it is consumed.
1850-
impl<A: Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
1880+
impl<A: Integer + Ord + Clone + ToPrimitive> DoubleEndedIterator<A> for Range<A> {
18511881
#[inline]
18521882
fn next_back(&mut self) -> Option<A> {
18531883
if self.stop > self.state {
@@ -1868,11 +1898,12 @@ pub struct RangeInclusive<A> {
18681898

18691899
/// Return an iterator over the range [start, stop]
18701900
#[inline]
1871-
pub fn range_inclusive<A: Add<A, A> + Ord + Clone + One>(start: A, stop: A) -> RangeInclusive<A> {
1901+
pub fn range_inclusive<A: Add<A, A> + Ord + Clone + One + ToPrimitive>(start: A, stop: A)
1902+
-> RangeInclusive<A> {
18721903
RangeInclusive{range: range(start, stop), done: false}
18731904
}
18741905

1875-
impl<A: Add<A, A> + Eq + Ord + Clone> Iterator<A> for RangeInclusive<A> {
1906+
impl<A: Add<A, A> + Eq + Ord + Clone + ToPrimitive> Iterator<A> for RangeInclusive<A> {
18761907
#[inline]
18771908
fn next(&mut self) -> Option<A> {
18781909
match self.range.next() {
@@ -1904,7 +1935,8 @@ impl<A: Add<A, A> + Eq + Ord + Clone> Iterator<A> for RangeInclusive<A> {
19041935
}
19051936
}
19061937

1907-
impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for RangeInclusive<A> {
1938+
impl<A: Sub<A, A> + Integer + Ord + Clone + ToPrimitive> DoubleEndedIterator<A>
1939+
for RangeInclusive<A> {
19081940
#[inline]
19091941
fn next_back(&mut self) -> Option<A> {
19101942
if self.range.stop > self.range.state {
@@ -2184,6 +2216,7 @@ mod tests {
21842216

21852217
use cmp;
21862218
use uint;
2219+
use num;
21872220

21882221
#[test]
21892222
fn test_counter_from_iter() {
@@ -2801,12 +2834,51 @@ mod tests {
28012834

28022835
#[test]
28032836
fn test_range() {
2837+
/// A mock type to check Range when ToPrimitive returns None
2838+
struct Foo;
2839+
2840+
impl ToPrimitive for Foo {
2841+
fn to_i64(&self) -> Option<i64> { None }
2842+
fn to_u64(&self) -> Option<u64> { None }
2843+
}
2844+
2845+
impl Add<Foo, Foo> for Foo {
2846+
fn add(&self, _: &Foo) -> Foo {
2847+
Foo
2848+
}
2849+
}
2850+
2851+
impl Ord for Foo {
2852+
fn lt(&self, _: &Foo) -> bool {
2853+
false
2854+
}
2855+
}
2856+
2857+
impl Clone for Foo {
2858+
fn clone(&self) -> Foo {
2859+
Foo
2860+
}
2861+
}
2862+
2863+
impl num::One for Foo {
2864+
fn one() -> Foo {
2865+
Foo
2866+
}
2867+
}
2868+
28042869
assert_eq!(range(0i, 5).collect::<~[int]>(), ~[0i, 1, 2, 3, 4]);
2870+
assert_eq!(range(-10i, -1).collect::<~[int]>(), ~[-10, -9, -8, -7, -6, -5, -4, -3, -2]);
28052871
assert_eq!(range(0i, 5).invert().collect::<~[int]>(), ~[4, 3, 2, 1, 0]);
28062872
assert_eq!(range(200, -5).collect::<~[int]>(), ~[]);
28072873
assert_eq!(range(200, -5).invert().collect::<~[int]>(), ~[]);
28082874
assert_eq!(range(200, 200).collect::<~[int]>(), ~[]);
28092875
assert_eq!(range(200, 200).invert().collect::<~[int]>(), ~[]);
2876+
2877+
assert_eq!(range(0i, 100).size_hint(), (100, Some(100)));
2878+
// this test is only meaningful when sizeof uint < sizeof u64
2879+
assert_eq!(range(uint::max_value - 1, uint::max_value).size_hint(), (1, Some(1)));
2880+
assert_eq!(range(-10i, -1).size_hint(), (9, Some(9)));
2881+
assert_eq!(range(Foo, Foo).size_hint(), (0, None));
28102882
}
28112883

28122884
#[test]

0 commit comments

Comments
 (0)