I have a function that takes an argument of type u16. Is there an elegant way to define a custom data type that behaves exactly like a u16 but only
Unfortunately, there is no such a thing inside the std crate.
However, you can do it yourself in an optimized manner with the nightly generic consts. Example:
#![feature(const_generics)]
pub struct BoundedI32(i32);
impl BoundedI32<{LOW}, {HIGH}> {
pub const LOW: i32 = LOW;
pub const HIGH: i32 = HIGH;
pub fn new(n: i32) -> Self {
BoundedI32(n.min(Self::HIGH).max(Self::LOW))
}
pub fn fallible_new(n: i32) -> Result {
match n {
n if n < Self::LOW => Err("Value too low"),
n if n > Self::HIGH => Err("Value too high"),
n => Ok(BoundedI32(n)),
}
}
pub fn set(&mut self, n: i32) {
*self = BoundedI32(n.min(Self::HIGH).max(Self::LOW))
}
}
impl std::ops::Deref for BoundedI32<{LOW}, {HIGH}> {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let dice = BoundedI32::<1, 6>::fallible_new(0);
assert!(dice.is_err());
let mut dice = BoundedI32::<1, 6>::new(0);
assert_eq!(*dice, 1);
dice.set(123);
assert_eq!(*dice, 6);
}
And then you can implement the maths, etc.
If you want to chose the bound at runtime, you don't need this feature, and you just need to do something like that:
pub struct BoundedI32 {
n: i32,
low: i32,
high: i32,
}
You can also use a crate like bounded-integer that allows to generate a bounded integer on-the-fly with a macro.