User-defined range profiles for integer types?


#1

In Rust, char is basically u32 with some limits on what values it can take. This allows things like not having to check if char input is invalid range when converting to 1 to 4 UTF-8 bytes, for example.

Is there a way, without writing a compiler plug-in, to specify other similar range limits on integer types? For example, is it possible to define a profile of u16 that promises that the value can’t be a surrogate or that the value can’t be a surrogate or an ASCII value other than 0? Having the compiler check limits like these statically on literals assigned to such a profiled u16 would allow conversions to UTF-8 and UTF-16 without run-time branches to check for possibilities that have already been excluded at compile time.


#2

The only way to do anything vaguely like this in Rust is to define your own type with constructors that don’t let you construct invalid values.

Eg.

struct NonZeroU16 {
    val: u16,
}

impl NonZeroU16 {
    fn new(val: u16) -> Result<NonZeroU16, SomeError> {
        if val == 0 {
            return Err(SomeError);
        }
        Ok(NonZeroU16 {
            val: val,
        })
    }
}

Then if you have something like NonZeroU16::new(23).unwrap() in your code the compiler should be able optimize away the unwrap.

So basically no. You can do this in dependently typed languages though. In, eg. ATS/Idris you can write a type equivalent to struct NonZeroU16 { val: u16, proof_val_isnt_zero: val != 0 } and then use proof_val_isnt_zero to tell the compiler that it can eliminate branches of computation.


#3

The only way to do anything vaguely like this in Rust is to define your
own type with constructors that don’t let you construct invalid values.

I want to use profiled integers in arrays baked into the data segment of the resulting library. That seems to rule out constructors.

Thanks.


#4

I think you can still build your type erroneously like this:

let z = NonZeroU16 { val: 0 };

#5

That seems to rule out constructors

Maybe in the future you’ll be able to panic! in a const fn to raise a compilation error…

I think you can still build your type erroneously like this:

Not outside the module NonZeroU16 is defined in (so long as val is private).


#6

OK, still learning :slight_smile:


#7

related post: A question/remark on arithmetic operations