Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you specify value constraints in Rust?

I'm looking for a way to move type constraints into some kind of wrapper. As an example, in Ada you might see something like this:

type Element is Integer range 100 .. 1000;

Which is defining a new type Element that --- while still an Integer --- is bound to a specific range. There's also mod which will loop back around (super useful).

In Rust, so far I've been checking this manually within all my functions, ie:

if x < 100 || x >= 1000 {
    // throw some kind of error
}

But it would be really nice to instead define a new type that performs this check for me on assignment, similar to how integers can't overflow by default. I know that we don't have inheritance, but maybe there's some kind of trait I can implement?

TL;DR: I'm sure my method isn't best practice, but what's the standard alternative?

like image 568
nebulaeandstars Avatar asked Sep 09 '21 13:09

nebulaeandstars


1 Answers

But it would be really nice to instead define a new type that performs this check for me on assignment, similar to how integers can't overflow by default.

This would indeed be best.

You can define it as a user-type:

struct BoundedU16<const MIN: u16, const MAX: u16>(u16);

And then define all the methods you expect, starting with new:

impl<const MIN: u16, const MAX: u16> BoundedU16<MIN, MAX> {
    pub const fn new(value: u16) -> Result<Self, BoundError> {
        if value >= MIN && value <= MAX {
            Ok(Self(value))
        } else {
            Err(BoundError(value, MIN, MAX))
        }
    }
}

The main drawback is that at the moment you can't have BoundedInteger<T, const MIN: T, const MAX: T>. The work-around is to use a macro to define the multiple Bounded[I|U][8|16|32|64|size].

Then you can declare type Element = BoundedU16<100, 1000>;.

Though do note that any Element is just an alias, here, not a new type. If you declare a new type with struct Element(BoundedU16<100, 1000>) then you need to implement (or derive) all the traits again.


A trait would allow you to add a validate method, but would not allow you to implement Add, Sub, ... which automatically validate. It's a poorer solution.

like image 118
Matthieu M. Avatar answered Sep 28 '22 04:09

Matthieu M.