当操作数可能是值或引用时,如何为 Rust 结构实现运算符?

问题描述 投票:0回答:1

我对此很陌生,并尝试重载足够多的运算符(

Mul
Sub
)以在简单类型上获得一个简单的算术函数来编译:

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

铁锈游乐场

问题是我似乎无法理解我需要定义的所有不同实现 - 似乎不仅需要值而且还需要值引用的单独

impl
函数。一旦我实现了足够多的它们,我似乎最终得到了一个引用而不是一个值:

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

我很困惑为什么我不能写这么简单的东西——我错过了什么?

更令人担忧的是,如果我真的让它工作,我真的需要为每个运算符和每个组合实现 huge 数量的功能吗?

  • A @ B
  • A @ &B
  • &A @ B
  • &A @ &B

引用是否可以自动解引用为值,并使用运算符函数的值版本?毕竟它们是对不可变值的引用——值就在那里。

(我不确定是否要提及这一点,但我实际上是在

newtype
上使用 use derive_more::{Mul};,包装另一个元组类型,但
derive_more
似乎只包括对自动生成双值的支持二元运算符函数,而不是其他三个涉及一个或两个引用的函数,所以在这个问题中我已经恢复到基本的
struct
我看到了同样的问题)。

rust operator-overloading borrow-checker arithmetic-expressions
1个回答
0
投票

处理这个问题的一种方法是使用宏,这是标准库经常为与您类似的情况所做的事情。

在你的情况下,你可以这样做,例如:

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

tuple_mul!(Tuple, f64);
tuple_mul!(Tuple, &f64);
tuple_mul!(&Tuple, f64);
tuple_mul!(&Tuple, &f64);

与其他运营商类似。这样您就可以避免复制粘贴冗余代码,而是使用宏为您生成它并使您的代码更易于维护。

© www.soinside.com 2019 - 2024. All rights reserved.