User-defined range profiles for integer types?

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.

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.

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.

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

let z = NonZeroU16 { val: 0 };

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).

1 Like

OK, still learning :slight_smile:

related post: A question/remark on arithmetic operations