I can't work out how to have clean looking maths on structs without requiring that those struct values be copied everywhere.
If you wanted to have a struct that you could perform math on, you'd write something like this:
use std::ops::*;
struct Num {
i: i32,
}
impl Add for Num {
type Output = Num;
fn add(self, other: Num) -> Num {
Num {
i: self.i + other.i,
}
}
}
(This is a simplified example. An actual example might be doing Vector maths)
This lets us write nice a + (b / (c * d))
style code.
Due to borrowing semantics, the above code falls over as quickly as a + b + a
. Once a
is used once it can't be used again, as ownership was moved to the relevant function (i.e. add
).
The simple way of solving this is to implement Copy
for the struct:
#[derive(Copy)]
struct Num {
i: i32,
}
This means when Num
s are passed to add
, their values are automatically cloned so that they can be dropped cleanly.
But this seems inefficient! We don't need these structs to be duplicated all over the place: it's read-only, and we really just need it to be referenced to create the new structure we're returning.
This leads me to think that we should implement the math operations on references instead:
impl<'a> Add for &'a Num {
type Output = Num;
fn add(&'a self, other: &'a Num) -> Num {
Num {
i: self.i + other.i,
}
}
}
Now we have math operations where we aren't cloning data all over the place, but now our math looks gross! a + (b / (c * d))
now must be &a + &(&b / &(&c * &d))
. It doesn't help if you have your value type references either (eg let a = &Num { /* ... */ }
), because the return value of add
is still a Num
.
Is there a clean way to implement ops for structs such that math operations look clean, and the struct values aren't being copied everywhere?
Related: