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

huangapple go评论79阅读模式
英文:

How to implement operators for a Rust struct when the operands might be values or references?

问题

以下是您的代码的翻译部分:

#[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 {
    let a = dot(inc, normal);
    let b = 2.0 * a;
    let c = normal * b;
    inc - c
}

#[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 = v - n * 2.0 * dot(&v, &n);

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

请注意,我已经将HTML编码的字符(如&amp;)替换为正常字符。如果您需要进一步的翻译或解释,请告诉我。

英文:

I am new to this, and trying to overload enough operators (Mul, Sub) to get a simple arithmetic function on a simple type to compile:

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

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


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

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

impl std::ops::Sub&lt;&amp;Tuple&gt; for Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &amp;Tuple) -&gt; 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&lt;&amp;Tuple&gt; for &amp;Tuple {
    type Output = Tuple;
    fn sub(self, rhs: &amp;Tuple) -&gt; 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: &amp;Tuple, normal: &amp;Tuple) -&gt; Tuple {
    //inc - normal * 2.0 * inc.dot(&amp;normal)
    let a = dot(&amp;inc, &amp;normal);
    let b = 2.0 * a;
    let c = normal * b;
    inc - c    // &lt;--- compile error: expected `&amp;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(&amp;v, &amp;n);
        let r = v - n * 2.0 * dot(&amp;v, &amp;n);  // &lt;-- compile error: expected `&amp;Tuple`, found struct `Tuple`
//                  ~~~~~~~~~~~~~~~~~~~~~        

        println!(&quot;{:?}&quot;, r);
    }
}

Rust Playground

The problem is that I just can't seem to get my head around all the different implementations that I need to define - there seems to be separate impl functions required for not just values but also references to values. And once I do implement enough of them, I seem to end up with a reference instead of a value:

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

I'm perplexed as to why I can't write something this simple - what am I missing?

More worryingly, if I do get this to work, do I really need to implement a huge number of functions for not just each operator, but each combination?

  • A @ B
  • A @ &amp;B
  • &amp;A @ B
  • &amp;A @ &amp;B

Can a reference be automatically dereferenced into a value, and the value version of the operator function used? They are references to immutable values after all - the values are right there.

(I wasn't sure whether to mention this, but I was actually using use derive_more::{Mul}; on a newtype, wrapping another tuple type, but derive_more only seems to include support for automatic generation of the both-are-values binary operator functions, not the other three involving one or two references, so in this question I've reverted back to a basic struct where I see the same sort of problem).

答案1

得分: 4

在您的情况下,您可以使用宏来处理,这是标准库经常用于类似情况的方法之一。

例如,您可以这样做:

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

以及其他操作符类似。这样,您可以避免复制粘贴冗余的代码,而是使用宏为您生成代码,使您的代码更易维护。

英文:

One way to handle this would be to use macros, which is something the standard library does quite often for cases similar to yours.

In your case, you could do, e.g.:

macro_rules! tuple_mul {
( $lhs:ty , $rhs:ty ) =&gt; {
impl std::ops::Mul&lt;$rhs&gt; for $lhs {
type Output = Tuple;
fn mul(self, rhs: $rhs) -&gt; 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, &amp;f64);
tuple_mul!(&amp;Tuple, f64);
tuple_mul!(&amp;Tuple, &amp;f64);

And similar for other operators. This way you can avoid copy-pasting the redundant code, instead using macros to generate it for you and make your code more maintainable.

huangapple
  • 本文由 发表于 2023年4月11日 11:48:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75982257.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定