Search code examples
rustoperator-overloading

Is there a way to overwrite the assignment operator for a Rust type?


My problem is the following: I'd like to set up a primitive-like struct with a more limited value range. As in

#[repr(transparent)]
struct MyLimitedInt {
  v: i8,
}

but ensuring that v's value is always between -10 and 10. I know that I can implement std::ops traits in order to check the value on addition etc., but there does not seem to be a way of constructing an instance of MyLimitedInt in a primitive-like way while still checking bounds (as in let my_v: MyLimitedInt = -12;, which should limit the value to -10).

In C-like languages I could probably do that by overwriting the type's = operator, but is there a way to achieve a similar result in Rust without requiring more verbose constructors or setters?


Solution

  • The assignment operator can't be overloaded in Rust. However, you can overload other operators or use methods instead - for example:

    use std::cmp::{min, max};
    
    #[repr(transparent)]
    struct MyLimitedInt {
      v: i8,
    }
    
    impl MyLimitedInt {
      pub fn from_clamped(value: i8) -> Self {
        Self { v: min(10, max(value, -10))
      }
    
      /// Sets the value of the int, constraining it to the range [-10, 10]
      pub fn set_clamped(&mut self, value: i8) {
        *self = Self::from_clamped(value);
      }
    }
    

    This can be extended with overloads for the arithmetic operators to make it usable more like primitives:

    use std::ops::Add;
    
    impl Add for MyLimitedInt {
      type Output = Self;
    
      fn add(self, other: Self) -> Self {
        Self::from_clamped(self.value + other.value)
      }
    }
    
    impl Add<i32> for MyLimitedInt {
      type Output = Self;
    
      fn add(self, other: i32) -> Self {
        Self::from_clamped(self.value + other.value)
      }
    }
    

    This would make it possible to use the addition operator with MyLimitedInt, clamping the result automatically:

    let x = MyLimitedInt::from_clamped(20) + 5; // 10
    let y = MyLimitedInt::from_clamped(-20) + x; // 0