The obvious answer is yes. After all, Range<u32> is ExactSizeIterator and Range<char> is strictly smaller, so it should fit into ExactSizeIterator, right?
The reality is, as always, more complicated.
Strictly, Range<u32> should not be ExactSizeIterator, because (0..=u32::MAX).len() will overflow on 16 bit systems where usize = u16. The same argument applies to Range<char>, so, strictly, Range<char> should not be ExactSizeIterator.
The difference with Range<u32> is that it's been ExactSizeIterator since 1.0.0, so removing it would be breaking. With Range<char>, we can choose to do the "correct" thing, or the convenient thing pointing to the precident of u32.
(Note that this also applies to RangeInclusive<u16> and RangeInclusive<char>!)
Does it use #[rustc_inherit_overflow_checks], Add::add, or neither of those and just +?
Because the standard library is always compiled in release mode, unless special effort is given to opt in to the "inherit overflow checks" hacks, arithmetic in the standard library is going to use the release profile of wrapping on overflow.
It'd be nice if someone could confirm the behavior here, though, as I don't really understand how these hacks work
The code comment explaining why the ExactSizeIterator impls are wrong and give the wrong result are originally @SimonSapin's, though they have my name on git blame IIRC.
fn len(&self) -> usize {
let (lower, upper) = self.size_hint();
// Note: This assertion is overly defensive, but it checks the invariant
// guaranteed by the trait. If this trait were rust-internal,
// we could use debug_assert!; assert_eq! will check all Rust user
// implementations too.
assert_eq!(upper, Some(lower));
lower
}
The assert is what will cause the panic IMO.
The size_hint comes from here:
impl<A: Step> Iterator for ops::Range<A> {
type Item = A;
// ...
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.start < self.end {
let hint = Step::steps_between(&self.start, &self.end);
(hint.unwrap_or(usize::MAX), hint)
} else {
(0, Some(0))
}
}
// ...
}
So all-in-all for (0..u32::MAX).len()[you mistakenly used ..= which I just noticed] you need to calculate usize::try_from(u32::MAX-0) which is None and then assert that this None equals u32::MAX which it doesn’t.