我对此很陌生,并尝试重载足够多的运算符(
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
我看到了同样的问题)。
处理这个问题的一种方法是使用宏,这是标准库经常为与您类似的情况所做的事情。
在你的情况下,你可以这样做,例如:
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);
与其他运营商类似。这样您就可以避免复制粘贴冗余代码,而是使用宏为您生成它并使您的代码更易于维护。