Я новичок в этом и пытаюсь перегрузить достаточно операторов (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`
Я недоумеваю, почему я не могу написать что-то настолько простое — что я упускаю?
Что еще более тревожно, если я заставлю это работать, действительно ли мне нужно будет реализовывать огромное количество функций не только для каждого оператора, но и для каждой комбинации?
A @ B
A @ &B
&A @ B
&A @ &B
Можно ли автоматически разыменовать ссылку в значение и использовать версию значения операторной функции? В конце концов, это ссылки на неизменяемые значения — значения прямо здесь.
(Я не был уверен, стоит ли упоминать об этом, но на самом деле я использовал 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);
И аналогично для других операторов. Таким образом, вы можете избежать копирования избыточного кода, вместо этого используя макросы, чтобы сгенерировать его для вас и сделать ваш код более удобным в сопровождении.