You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust结构体操作数为值或引用时,如何实现操作符重载?

自定义Tuple结构体操作符重载问题

我是Rust新手,正尝试重载Mul、Sub等操作符,使自定义Tuple结构体的简单算术函数能够编译。现有代码如下:

#[derive(Debug)]
struct Tuple {
    x: f64, y: f64, z: f64, w: f64,
}

fn dot(a: &Tuple, b: &Tuple) -> f64 {
    (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w)
}


impl std::ops::Mul<f64> for Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        Tuple{x: self.x * rhs, y: self.y * rhs,
              z: self.z * rhs, w: self.w * rhs}
    }
}

impl std::ops::Mul<f64> for &Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        Tuple{x: self.x * rhs, y: self.y * rhs,
              z: self.z * rhs, w: self.w * rhs}
    }
}

impl std::ops::Sub<&Tuple> for Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        Tuple{x: self.x - rhs.x, y: self.y - rhs.y,
              z: self.z - rhs.z, w: self.w - rhs.w}
    }
}

impl std::ops::Sub<&Tuple> for &Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        Tuple{x: self.x - rhs.x, y: self.y - rhs.y,
              z: self.z - rhs.z, w: self.w - rhs.w}
    }
}

fn reflect(inc: &Tuple, normal: &Tuple) -> Tuple {
    //inc - normal * 2.0 * inc.dot(&normal)
    let a = dot(&inc, &normal);
    let b = 2.0 * a;
    let c = normal * b;
    inc - c    // <--- compile error: expected `&Tuple`, found struct `Tuple`
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn something() {
        let v = Tuple { x: 1.0, y: -1.0, z: 0.0, w: 0.0 };
        let n = Tuple { x: 0.0, y: 1.0, z: 0.0, w: 0.0 };

        //let r = reflect(&v, &n);
        let r = v - n * 2.0 * dot(&v, &n);  // <-- compile error: expected `&Tuple`, found struct `Tuple`
//                  ~~~~~~~~~~~~~~~~~~~~~        

        println!("{:?}", r);
    }
}

编译错误信息:

error[E0308]: mismatched types
  --> lib/tuple.rs:70:11
   |
70 |     inc - c
   |           ^
   |           |
   |           expected `&Tuple`, found struct `Tuple`
   |           help: consider borrowing here: `&c`

我搞不懂需要定义的各种实现,不知道是不是必须为A @ B、A @ &B、&A @ B、&A @ &B每种组合分别实现操作符,同时疑惑不可变引用能不能自动解引用为值,从而复用值版本的操作符。之前我在newtype上用过derive_more::{Mul},但它只生成值对值的二元操作符函数。


问题根源

你当前的Sub实现仅支持**左侧为Tuple/&Tuple、右侧为&Tuple**的场景,但inc - c中inc是&Tuple,c是Tuple值,不符合已实现的方法签名,因此触发类型不匹配错误。

Rust不会自动将值转为引用,也不会自动将不可变引用解引用为值(这涉及所有权转移,编译器不会擅自做主),所以确实需要覆盖操作数的四种组合,但可以通过复用核心逻辑减少重复代码。

解决步骤

1. 为Tuple添加Copy/Clone特性

因为Tuple的字段都是f64(已实现Copy),直接派生Copy和Clone,这样引用可以安全地解引用为值,无需手动克隆:

#[derive(Debug, Copy, Clone)]
struct Tuple {
    x: f64, y: f64, z: f64, w: f64,
}

2. 优先实现值对值的核心操作

先写值版本的操作符实现,作为核心逻辑,其他版本都基于它扩展:

// 值对f64的Mul(核心)
impl std::ops::Mul<f64> for Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        Tuple {
            x: self.x * rhs,
            y: self.y * rhs,
            z: self.z * rhs,
            w: self.w * rhs,
        }
    }
}

// 值对值的Sub(核心)
impl std::ops::Sub for Tuple {
    type Output = Tuple;
    fn sub(self, rhs: Tuple) -> Tuple {
        Tuple {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            w: self.w - rhs.w,
        }
    }
}

3. 复用核心实现,快速覆盖其他组合

基于值版本的实现,轻松完成剩余三种组合的操作符:

// 引用对f64的Mul:解引用self调用核心实现
impl std::ops::Mul<f64> for &Tuple {
    type Output = Tuple;
    fn mul(self, rhs: f64) -> Tuple {
        (*self).mul(rhs)
    }
}

// 值对引用的Sub:解引用rhs调用核心Sub
impl std::ops::Sub<&Tuple> for Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        self - *rhs
    }
}

// 引用对值的Sub:解引用self调用核心Sub
impl std::ops::Sub<Tuple> for &Tuple {
    type Output = Tuple;
    fn sub(self, rhs: Tuple) -> Tuple {
        (*self) - rhs
    }
}

// 引用对引用的Sub:解引用两边调用核心Sub
impl std::ops::Sub<&Tuple> for &Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &Tuple) -> Tuple {
        (*self) - (*rhs)
    }
}

现在reflect函数里的inc - c和测试代码中的v - n * 2.0 * dot(&v, &n)都能正常编译运行。

4. 用derive_more简化代码

derive_more支持通过属性生成引用相关的实现,不用手动写重复代码:

use derive_more::{Mul, Sub};

#[derive(Debug, Copy, Clone, Mul, Sub)]
#[mul(ref)]    // 生成引用对f64的Mul
#[sub(ref_all)] // 生成所有引用组合的Sub
struct Tuple {
    x: f64, y: f64, z: f64, w: f64,
}

#[sub(ref_all)]会自动生成值-引用、引用-值、引用-引用三种Sub实现,#[mul(ref)]生成引用对f64的Mul实现,大幅减少手写代码量。


内容的提问来源于stack exchange,提问作者davidA

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:54:58