【问题标题】:How can an operator be overloaded for different RHS types and return values?如何为不同的 RHS 类型和返回值重载运算符?
【发布时间】:2014-08-26 23:28:35
【问题描述】:

给定以下结构:

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 { 
...

对于其他实现,反之亦然。

【问题讨论】:

  • @ChrisMorgan 这并不是真正的重复。这个问题有两个具体的impls,而那个问题有一个通用的impl 和一个具体的。

标签: operator-overloading rust


【解决方案1】:

目前每个 trait-type 对只允许一个 impl

RFC 48改善这种情况,但这不是完整的故事(这不是真正的故事)。相关部分是Coherence,当然也没有具体提到运算符重载的情况,本质上说还是违法的:

下面的例子不行:

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

Niko Matsakis(该 RFC 和 Rust 类型系统专家的作者)一直在专门考虑这些重载特征:他是 published(“如果我想要重载怎么办?”)下面的把戏,但是他表达了对它的反感,并提到他希望允许您编写的实现...

...这是他的RFC 135进来的地方。情况在"multidispatch traits"中有详细描述。


您现在可以使用次要特征来解决它。额外的 trait 层允许您只编写一个 impl Mul&lt;...&gt; for Vector3D,但代价是您希望拥有多个 Mul 实现的每种类型都需要一个新的 trait。

#[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
}

【讨论】:

  • 这个解决方案似乎已经过时并且给出了编译器错误。 Shepmaster 的回答似乎符合要求。
【解决方案2】:

从 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多