Cannot implement Add for primitive types

Hello,

I can't find a way to implement Add when primitive type is on the left side of addition:

#[derive(Clone, Copy, Debug, PartialEq)]
struct Point<T> {
    x: T,
    y: T,
}
impl<T> Add<T> for Point<T>
where
    T: Copy + PartialEq + Default + Add<Output = T> + PartialOrd,
{
    type Output = Point<T>;

    fn add(self, right: T) -> Point<T> {
        Point {
            x: self.x + right,
            y: self.y + right,
        }
    }
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    #[test]
    fn test_add_Point_and_f32() {
        assert_eq!(Point { x: 1., y: 1. } + 1., Point { x: 2., y: 2. });
    }
    #[test]
    fn test_add_f32_and_Point() {
        assert_eq!(1. + Point { x: 1., y: 1. }, Point { x: 2., y: 2. });
    }
}

The first test is working as expected, but the second one failed.
Why rust compiler doesn't guess that the order doesn't change the final result ?
I need to add f32 from any side, but rust restricts things to current crate. Such a restriction should be able to be bypassed by an attribute.

Thank you in advance for your help.

1 Like

first, addition is not necessarily commutative. more importantly, the + operator desugars to a method call with different type for the self receiver.

the "restriction" you mentioned is called coherent rule. it's necessary to to ensure the type system can be locally checked.

for this particular case, it's might be possible to implement the Add trait for the concrete type (e.g. f32 or f64), but I don't think a blanket implementation is possible due to coherence.

impl Add<Point<f64>> for f64 {
    type Output = Point<f64>;
    fn add(self, rhs: Point<f64>) -> Point<f64> {
        Point {
            x: self + rhs.x,
            y: self + rhs.y,
        }
    }
}

#[test]
fn test_add_f32_and_Point() {
    assert_eq!(1. + Point { x: 1., y: 1. }, Point { x: 2., y: 2. });
}
1 Like

It’s inherently limited this way, you cannot have a generic impl Add<MyType> for T where T: Bound with Rust’s orphan rules.

Existing crates like ndarray handle this by only implementing the other way fully generically. For example for multiplication of a vector and a scalar, they generically implement Mul<B> for Array…, but for the other way, they only have a list of concrete implementations like Mul<Array…> for f64 or Mul<Array…> for i32 or Mul<Array…> for u8 etc… for the most common scalar types, generated by macro.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.