After the Step
trait was reworked, it's now possible (and fairly plausible) to implement Step
for char
, allowing RangeInclusive<char>
(and other ranges) to just work. I've already implemented character ranges twice before, because I've familiarized myself with workloads that appreciate having nice range types.
Does this sound like something we should add to the standard library? It would be insta-stable. This would be the first type usable in Range
with a noncontinuous domain.
const SURROGATE_RANGE: ops::Range<u32> = 0xD800..0xE000;
unsafe impl Step for char {
fn steps_between(&l: &Self, &r: &Self) -> Option<usize> {
let l = l as u32;
let r = r as u32;
if l >= r {
let diff = r - l;
if l < SURROGATE_RANGE.start && SURROGATE_RANGE.end < r {
Some(diff as usize - SURROGATE_RANGE.len())
} else {
Some(diff as usize)
}
} else {
None
}
}
fn forward_checked(c: Self, n: usize) -> std::option::Option<Self> {
let c = c as u32;
let mut r = Step::forward_checked(c, n)?;
if c < SURROGATE_RANGE.start && SURROGATE_RANGE.start <= r {
r = Step::forward_checked(r, SURROGATE_RANGE.len())?;
}
if r <= char::MAX as u32 {
Some(unsafe { char::from_u32_unchecked(r) })
} else {
None
}
}
fn backward_checked(c: Self, n: usize) -> std::option::Option<Self> {
let c = c as u32;
let mut r = Step::backward_checked(c, n)?;
if c > SURROGATE_RANGE.end && SURROGATE_RANGE.end >= r {
r = Step::backward_checked(r, SURROGATE_RANGE.len())?;
}
Some(unsafe { char::from_u32_unchecked(r) })
}
}
godbolt (EDIT: actually linked the godbolt example of this code)