How to create a numeric type with a limited range of types?

In Rust, I need a numeric type with the property to have a symmetric region around 0. If the number n is a valid value, then the number -n must also be real. How can I ensure type safety during initialization and arithmetic? How would it be better to implement modular and rich arithmetic by type?


The simplest example of a problem:

type MyNumber = i8; // Bound to domain (-100, 100)

fn main() {
    let a = MyNumber(128); // Doesn't panic when 128 > 100
}

There are several considerations, and I tried to find various solutions. I will avoid general programming for examples of them below:

  • Based on the off enum type, ensure that only valid values ​​are valid values. It gets very dirty:

    enum MyNumber {
        One,
        Two,
        ...
    }
    impl MyNumber {
        fn convert(i8) -> MyNumber {
            match {
                1 => MyNumber::One,
                2 => MyNumber::Two,
                ...
            }
        }
    }
    
  • , , . struct.

  • ( ) , . , , .

    extern crate num;
    
    use num::Bounded;
    use std::cmp;
    struct MyNumber {
        val: i8,
    }
    
    impl Bounded for MyNumber {
        fn max_value() -> Self {
            MyNumber { val: 65 }
        }
        fn min_value() -> Self {
            MyNumber { val: -50 }
        }
    }
    impl MyNumber {
        fn clamp(&mut self) {
            self.val = cmp::min(MyNumber::max_value().val, 
                                cmp::max(MyNumber::min_value().val, self.val))
        }
        fn add(&mut self, mut addend: Self) {
            self.clamp();
            addend.clamp(); 
            //TODO: wrap or saturate result
            self.val = self.val + addend.val
        }
    }
    
    fn main() {
        let mut a = MyNumber { val: i8::max_value() };
        let b = MyNumber { val: i8::min_value() };
        a.add(b);
        println!("{} + {} = {}",
                 MyNumber::max_value().val,
                 MyNumber::min_value().val, 
                 a.val);
    }
    

- - , . !

, / ?

EDIT: 2014. , , Rust prefa, 1.0. , Python 2 3.

+4
1

, , . struct constructor.

, .

Rust ... , :

mod mynumber {
    // The struct is public, but the fields are not.
    // Note I've used a tuple struct, since this is a shallow
    // wrapper around the underlying type.
    // Implementing Copy since it should be freely copied,
    // Clone as required by Copy, and Debug for convenience.
    #[derive(Clone,Copy,Debug)]
    pub struct MyNumber(i8);

impl , i8, saturating_add, , . pub fn new, Option<MyNumber>, .

    impl MyNumber {
        fn is_in_range(val: i8) -> bool {
            val >= -100 && val <= 100
        }
        fn clamp(val: i8) -> i8 {
            if val < -100 {
                return -100;
            }
            if val > 100 {
                return 100;
            }
            // Otherwise return val itself
            val
        }
        pub fn new(val: i8) -> Option<MyNumber> {
            if MyNumber::is_in_range(val) {
                Some(MyNumber(val))
            } else {
                None
            }
        }

        pub fn add(&self, other: MyNumber) -> MyNumber {
            MyNumber(MyNumber::clamp(self.0.saturating_add(other.0)))
        }
    }
}

use :

use mynumber::MyNumber;

- :

fn main() {
    let a1 = MyNumber::new(80).unwrap();
    let a2 = MyNumber::new(70).unwrap();
    println!("Sum: {:?}", a1.add(a2));
    // let bad = MyNumber(123); // won't compile; accessing private field
    let bad_runtime = MyNumber::new(123).unwrap();  // panics
}

, , std::ops::Add .., a1 + a2 .

+6

Source: https://habr.com/ru/post/1666121/


All Articles