# ExactSizeIterator for Range\<char\>

**URL:** https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574
**Category:** libs
**Created:** [June 18, 2020, 2:22am UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574 "2020-06-18T02:22:16Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [June 18, 2020, 2:22am UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/1 "2020-06-18T02:22:17Z")

</div>

Should we have it?

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>`!)

[Here's the relevant impl lines on docs](https://doc.rust-lang.org/nightly/src/core/iter/range.rs.html#542-571) ([or permalinked on GitHub](https://github.com/rust-lang/rust/blob/2935d294ff862fdf96578d0cbbdc289e8e7ba81c/src/libcore/iter/range.rs#L542-L571)).

---

<div class="post-metadata">

### Author: ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)
#### Post date: [June 18, 2020, 6:56am UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/2 "2020-06-18T06:56:08Z")

</div>

> [@CAD97](#):
>
> `(0..=u32::MAX).len()` will overflow on 16 bit systems

I don’t have a 16bit system at hand to check but judging by the source code in the standard library (AFAICT) it would actually panic and not overflow.

---

<div class="post-metadata">

### Author: ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)
#### Post date: [June 18, 2020, 5:10pm UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/3 "2020-06-18T17:10:28Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)
#### Post date: [June 18, 2020, 5:59pm UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/4 "2020-06-18T17:59:28Z")

</div>

> [@CAD97](#):
>
> Does it use `#[rustc_inherit_overflow_checks]` , `Add::add` , or neither of those and just `+` ?

It ultimately uses `TryFrom<u32> for usize`.

---

<div class="post-metadata">

### Author: ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)
#### Post date: [June 18, 2020, 6:11pm UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/5 "2020-06-18T18:11:32Z")

</div>

To elaborate: the `ExactSizeIterator for Range<u32>` comes from the `range_exact_iter_impl` macro which does

```rust
impl ExactSizeIterator for ops::Range<$t> { }

```

so it [uses](https://doc.rust-lang.org/nightly/src/core/iter/traits/exact_size.rs.html#71-134)

```rust
    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](https://doc.rust-lang.org/nightly/src/core/iter/range.rs.html#486-540):

```rust
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))
        }
    }
    // ...
}

```

Which uses the [`Step` impl for `u32`](https://doc.rust-lang.org/nightly/src/core/iter/range.rs.html#401):

```rust
#[cfg(target_pointer_width = "16")]
step_integer_impls! {
    narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize];
    wider than usize: [u32 i32], [u64 i64], [u128 i128];
}

```

And this macro does (for the unsigned wider types like `u32` on 16 bit):

```rust
                #[inline]
                fn steps_between(start: &Self, end: &Self) -> Option<usize> {
                    if *start <= *end {
                        usize::try_from(*end - *start).ok()
                    } else {
                        None
                    }
                }

```

* * *

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.

---

<div class="post-metadata">

### Author: ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)
#### Post date: [June 18, 2020, 6:15pm UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/6 "2020-06-18T18:15:41Z")

</div>

> [@CAD97](#):
>
> `ExactSizeIterator` impls are wrong and give the wrong result

I guess a panic is considered a wrong result for an `ExactSizeIterator` impl.

---

<div class="post-metadata">

### Author: ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)
#### Post date: [September 16, 2020, 6:16pm UTC](https://internals.rust-lang.org/t/exactsizeiterator-for-range-char/12574/7 "2020-09-16T18:16:02Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
