Hacker News new | ask | show | jobs
by ridiculous_fish 2589 days ago
RangeInclusive is an interesting API:

1. How does this handle ranges whose length is larger than can be represented in an integer?

2. How does iterating a range work for floats? It looks like it just adds one [1], won't this mean that it will loop forever if the next representable float is +2?

1: https://doc.rust-lang.org/src/core/iter/range.rs.html#297

2 comments

Note this bit:

    impl<A: Step> Iterator for ops::RangeInclusive<A> {
RangeInclusive<A> implements Iterator only for types A which implement Step. Floating point types like f64 are not Step:

https://doc.rust-lang.org/std/iter/trait.Step.html

Thus RangeInclusive<f64> is perfectly valid, but RangeInclusive<f64> cannot be used as an Iterator.

See also the bounds on contains():

https://doc.rust-lang.org/std/ops/struct.RangeInclusive.html...

f64 is PartialOrd<f64>, so a RangeInclusive<f64> can be asked if it contains a specified f64. This definition would also allow one to ask a RangeInclusive<IpAddr> if it contains a specified IpAddr _or_ Ipv4Addr _or_ Ipv6Addr, since IpAddr is PartialOrd<IpAddr>, PartialOrd<Ipv4Addr>, and PartialOrd<Ipv6Addr>.

Any type A which can be compared to any other type B automatically gets RangeInclusive<A>::contains(B). Anything which doesn't can still be a RangeInclusive<A>, it just won't have contains().

1. It doesn't.

2. The Iterator implementation for RangeInclusive requires the underlying type to implement Step [1], which floats do not. So you can't iterate over a range of floats (although you can still construct one).

[1] https://doc.rust-lang.org/nightly/std/iter/trait.Step.html

As trivia, there does exist a well defined "next FP value" [1] for floats too but its behaviour might be unproductive here.

[1] https://doc.rust-lang.org/1.0.0/std/primitive.f64.html#metho...

What do you mean by "it doesn't?" For example say I construct a range [INT_MIN, INT_MAX] which should have a length of 2^32. Am I prohibited from constructing this range?
I misinterpreted your comment; I thought you meant ranges that were so large they couldn't fit in any integer type (presumably of floats).

But actually, it's weirder than I thought.

RangeInclusive doesn't have an inherent len() method, but one is provided by the ExactSizeIterator trait, which is implemented only when the underlying type is i8, u8, i16, or u16. len() always returns usize, regardless of the original type; thus I believe the reason for that particular list of types is that usize is only guaranteed to be at least 16 bits. But I'm not sure why it's not also implemented for RangeInclusive<usize> and RangeInclusive<isize>...

RangeInclusive is inclusive. This means `0..=0` has a length of 1. Therefore `0..=usize::MAX` has a length of usize::MAX + 1.
Nice, thanks for the complete answer. It's a highly principled (and perhaps frustrating) design, in keeping with Rust's ethos.

By way of comparison, Swift overflows and returns a length of 0. Oh well.

You’d use the plain `Range` type for that. https://doc.rust-lang.org/std/ops/struct.Range.html