1

I've got a newtype:

struct NanoSecond(u64);

I want to implement addition for this. (I'm actually using derive_more, but here's an MCVE.)

impl Add for NanoSecond {
    fn add(self, other: Self) -> Self {
        self.0 + other.0
    }
}

But should I implement AddAssign? Is it required for this to work?

let mut x: NanoSecond = 0.to();
let y: NanoSecond = 5.to();
x += y;

Will implementing it cause unexpected effects?

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62

1 Answers1

3

Implementing AddAssign is indeed required for the += operator to work.

The decision of whether to implement this trait will depend greatly on the actual type and kind of semantics that you are aiming for. This applies to any type of your own making, including newtypes. The most important prior is predictability: an implementation should behave as expected from the same mathematical operation. In this case, considering that the addition through Add is already well defined for that type, and nothing stops you from implementing the equivalent operation in-place, then adding an impl of AddAssign like so is the most predictable thing to do.

impl AddAssign for NanoSecond {
    fn add_assign(&mut self, other: Self) {
        self.0 += other.0
    }
}

One may also choose to provide additional implementations for reference types as the second operand (e.g. Add<&'a Self> and AddAssign<&'a Self>).

Note that Clippy has lints which check whether the implementation of the arithmetic operation is sound (suspicious_arithmetic_impl and suspicious_op_assign_impl). As part of being predictable, the trait should behave pretty much like the respective mathematical operation, regardless of whether + or += was used. To the best of my knowledge though, there is currently no lint or API guideline suggesting to implement -Assign traits alongside the respective operation.

E_net4
  • 27,810
  • 13
  • 101
  • 139
  • So… In Rust, a `mut`able `u64` actually changes when you do `+=` on it? A mutable binding is a binding to a box that changes when you change its value, instead of the binding changing? – wizzwizz4 Jun 21 '19 at 14:06
  • Speaking of boxes here is a bit misguided. `std::box::Box` exists, but it's unrelated and not applicable here. But yes, as `add_assign` receives `self` by mutable reference, the implementation is indeed expected to modify the referenced value in some way, and the binding does not change. See also: https://stackoverflow.com/q/28587698/1233251 – E_net4 Jun 21 '19 at 14:14
  • Is it possible to derive `AddAssign` from `Add`? I'd expect to automatically get an implementation of `*self = self + other` – BallpointBen Sep 17 '22 at 03:02