如何为不同的RHS类型和返回值重载运算符?

31

给定以下结构体:

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

我希望重载*操作符在右侧是一个Vector3D时可以进行点积运算,在右侧是一个f32时可以进行逐元素乘法。我的代码如下:

// Multiplication with scalar
impl Mul<f32, Vector3D> for 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, f32> for Vector3D {
    fn mul(&self, other: &Vector3D) -> f32 {
        self.x * other.x + self.y * other.y + self.z * other.z
    }   
}

编译器针对第一个 impl 块发出如下提示:

Vector3D.rs:40:1: 44:2 error: conflicting implementations for trait `std::ops::Mul`
Vector3D.rs:40 impl Mul<f32, Vector3D> for Vector3D { 
...
Vector3D.rs:53:1: 57:2 note: note conflicting implementation here
Vector3D.rs:53 impl Mul<Vector3D, f32> for Vector3D { 
...

对于另一个实现,情况相反。


这是 https://dev59.com/pYHba4cB1Zd3GeqPNzLm 的副本。 - Chris Morgan
1
@ChrisMorgan 这并不是完全重复的问题。这个问题有两个具体的 impl,而那个问题有一个通用的 impl 和一个具体的 impl - huon
2个回答

48

从Rust 1.0开始,现在您可以实现这个:

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);
}

引入了关联类型的大改变,这体现在每个实现中的 type Output = 部分。另一个值得注意的变化是运算符特性现在通过值传递参数并消耗它们,所以我继续为该结构体实现了 Copy


16

目前每个特质-类型对只允许一个impl

这种情况将通过RFC 48得到改善,但这并不是全部(实际上也不是任何一部分)的故事。相关部分是Coherence,它肯定没有特别提到操作符重载的情况,并且基本上说仍然是非法的:

The following example is NOT OK:

trait Iterator<E> { ... }
impl Iterator<char> for ~str  { ... }
impl Iterator<u8> for ~str { ... }
Niko Matsakis(该RFC的作者和Rust类型系统专家)一直在思考这些重载特性:他是下面这个技巧的发布者(“如果我想要重载怎么办?”),但他表达了对它的厌恶,并提到他想允许像你所写的实现...这就是他的RFC 135的作用。该情况在“多分派特性”中详细描述。
您现在可以使用次要特征进行解决。额外的特征层允许您仅编写一个impl Mul<...> for Vector3D,但代价是需要为每种类型都需要新的特征才能实现多个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
}

2
这个解决方案似乎已经过时并且会导致编译错误。Shepmaster的答案似乎很合适。 - progician

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接