4 ms·
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 ran
by ridiculous_fish 7y 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 https://doc.rust-lang.org/src/core/iter/range.rs.html#297
- comex 7y ago1. 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 https://doc.rust-lang.org/nightly/std/iter/trait.Step.html
- ridiculous_fish 7y agoWhat 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?
- comex 7y agoI 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>...
- ridiculous_fish 7y agoNice, 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.
- eridius 7y agoRangeInclusive is inclusive. This means `0..=0` has a length of 1. Therefore `0..=usize::MAX` has a length of usize::MAX + 1.
- bbatha 7y agoYou’d use the plain `Range` type for that. https://doc.rust-lang.org/std/ops/struct.Range.html https://doc.rust-lang.org/std/ops/struct.Range.html
- fulafel 7y agoAs 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#method.next_after https://doc.rust-lang.org/1.0.0/std/primitive.f64.html#metho...
- willglynn 7y agoNote 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 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#method.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().