I am trying to implement overloaded arithmetic operations for a struct D<T>:
struct D<T> {
reg: f64,
deriv: T,
}
If T: Copy like another f64 everything is fine, I just implement the Add trait straightforwardly.
However I'd like to have T be a type that is not (necessarily) copyable. E.g. it may be some recursively defined enum.
I can't seem to find a good way to implement Add now without getting into trouble at usage - in particular ideally the D<T> values are used exactly like f64 could be used. I tried implementing Add for all combinations of D<T> and &D<T> (which by itself felt pretty cumbersome, but would be ok if it worked):
impl<T> Add for D<T>
where
T: Clone + Add<T, Output = T>,
{
type Output = D<T>;
fn add(self, D { reg, deriv }: Self) -> Self::Output {
D {
reg: self.reg + reg,
deriv: self.deriv + deriv,
}
}
}
impl<T> Add for &D<T>
where
T: Clone + Add<T, Output = T>,
{
type Output = D<T>;
fn add(self, D { reg, deriv }: Self) -> Self::Output {
D {
reg: self.reg + reg,
deriv: self.deriv.clone() + deriv.clone(),
}
}
}
impl<T> Add<D<T>> for &D<T>
where
T: Clone + Add<T, Output = T>,
{
type Output = D<T>;
fn add(self, D { reg, deriv }: D<T>) -> Self::Output {
D {
reg: self.reg + reg,
deriv: self.deriv.clone() + deriv,
}
}
}
impl<T> Add<&D<T>> for D<T>
where
T: Clone + Add<T, Output = T>,
{
type Output = D<T>;
fn add(self, D { reg, deriv }: &D<T>) -> Self::Output {
D {
reg: self.reg + reg,
deriv: self.deriv + deriv.clone(),
}
}
}
But with that re-using the same D<T> twice still gets into trouble with the borrowchecker:
fn f<T>(x: &D<T>) -> D<T>
where
T: Clone + Add<T, Output = T>,
{
let y = x + x;
y + x + y + x
}
On the second usage of y:
use of moved value: `y`
value used here after move
To be clear I (think I) understand why rust thinks this is a problem - the first + moves the y - and it can be solved by borrowing :
fn f<T>(x: &D<T>) -> D<T>
where
T: Clone + Add<T, Output = T>,
{
let y = x + x;
&y + x + y + x
}
I've tried to make the output type of the Add instances references as well but then I run into lifetimespecifiers and "lifetime specifiers on associated types are unstable" and I don't actually think that's a good idea - I think I want whatever is calling the + to own the result, not just a reference to the result.
This all looks cumbersome, and it feels like I'm missing some pattern or approach here. Am I trying to have my cake and eat it too? Or maybe the resulting & is expected and accepted by rust programmers, and I'm worried about nothing here?