How can an operator be overloaded for different RHS types and return values?

后端 未结 2 818
既然无缘
既然无缘 2020-12-01 16:19

Given the following struct:

struct Vector3D {
    x: f32,
    y: f32,
    z: f32
}

I want to overload its * operator to do a d

相关标签:
2条回答
  • 2020-12-01 16:28

    As of Rust 1.0, you can now implement this:

    use std::ops::Mul;
    
    #[derive(Copy, Clone, PartialEq, Debug)]
    struct Vector3D {
        x: f32,
        y: f32,
        z: f32,
    }
    
    // Multiplication with scalar
    impl Mul<f32> for Vector3D {
        type Output = Vector3D;
    
        fn mul(self, f: f32) -> Vector3D {
            Vector3D {
                x: self.x * f,
                y: self.y * f,
                z: self.z * f,
            }
        }
    }
    
    // Multiplication with vector, aka dot product
    impl Mul<Vector3D> for Vector3D {
        type Output = f32;
    
        fn mul(self, other: Vector3D) -> f32 {
            self.x * other.x + self.y * other.y + self.z * other.z
        }
    }
    
    fn main() {
        let a = Vector3D {
            x: 1.0,
            y: 2.0,
            z: 3.0,
        };
        let b = a * -1.0;
        let c = a * b;
    
        println!("{:?}", a);
        println!("{:?}", b);
        println!("{:?}", c);
    }
    

    The big change that allows this is the introduction of associated types, which shows up as the type Output = bit in each implementation. Another notable change is that the operator traits now take arguments by value, consuming them, so I went ahead and implemented Copy for the struct.

    0 讨论(0)
  • 2020-12-01 16:34

    At the moment only a single impl is allowed per trait-type pair.

    This situation will be improved with RFC 48, but it's not the full story (it's not really any of the story). The relevant section is Coherence, and it certainly doesn't mention the operator overloading case specifically, and essentially says it is still illegal:

    The following example is NOT OK:

    trait Iterator<E> { ... }
    impl Iterator<char> for ~str  { ... }
    impl Iterator<u8> for ~str { ... }
    

    Niko Matsakis (author of that RFC & Rust-type-system expert) has been thinking about these overloading traits specifically: he is the one who published ("What if I want overloading?") the trick below, but he has expressed his distaste towards it, mentioning that he'd like to allow implementations as you have written...

    ... which is where his RFC 135 comes in. The situation is described in detail in "multidispatch traits".


    You can work-around it for now using secondary traits. The extra layer of traits allows you to write just one impl Mul<...> for Vector3D but comes at the cost of requiring a new trait for each type for which you wish to have multiple implementations of Mul.

    #[deriving(Show)]
    struct Vector3D {
        x: f32,
        y: f32,
        z: f32
    }
    
    trait MulVec3D<Res> {
        fn do_mul(&self, v: &Vector3D) -> Res;
    }
    
    // Multiplication with scalar
    impl MulVec3D<Vector3D> for f32 {
       fn do_mul(&self, v: &Vector3D) -> Vector3D {
           Vector3D {x: v.x * *self, y: v.y * *self, z: v.z * *self} 
       }
    }
    // Multiplication with vector, aka dot product
    impl MulVec3D<f32> for Vector3D {
        fn do_mul(&self, v: &Vector3D) -> f32 {
            self.x * v.x + self.y * v.y + self.z * v.z
        }
    }
    
    impl<Res, RHS: MulVec3D<Res>>  Mul<RHS, Res> for Vector3D {
        fn mul(&self, rhs: &RHS) -> Res {
            rhs.do_mul(self)
        }   
    }
    
    fn main() {
        let a = Vector3D { x: 1.0, y: 2.0, z: 3.0 };
        let b = Vector3D { x: -3.0, y: 2.0, z: -1.0 };
    
        println!("{}, {}", a * 2f32, a * b); // Vector3D { x: 2, y: 4, z: 6 }, -2
    }
    
    0 讨论(0)
提交回复
热议问题