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: &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);
}
}
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
--> lib/tuple.rs:70:11
|
70 | inc - c
| ^
| |
| expected `&Tuple`, found struct `Tuple`
| help: consider borrowing here: `&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 @ &B
&A @ B
&A @ &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).