#[derive(Debug)]
struct MathVector<T: Add + Copy> {
vec: Vec<T>,
}
you should not put bounds on types unless you need said bounds to specify the type's layout.
there is no good reason why you should require Copy at all, after all MathVector isn't even Copy, despite the fact that MathVector<MathVector<u8>> is perfectly sensible.
but even for Add, other types may want to store MathVector<T>, and by putting Add on the struct you force them to add the bound everywhere which is not only very annoying but can actually break very reasonable code.
consider :
enum MathOrNormal<T> {
Normal(Vec<T>),
Math(MathVector<T>),
}
here a MathOrNormal<T> would need Add even if it is always normal, for no good reason.
so you simply need
#[derive(Debug, Clone)]
struct MathVector<T> {
vec: Vec<T>,
}
your Add impl is also very much too restrictive.
you require all the values to be Ts, which closes you from many different valid inputs.
impl<T: Add<Output = T> + Copy> Add for MathVector<T> {
type Output = Self;
instead do something like
impl<U, T: Add<U>> Add<MathVector<U>> for MathVector<T> {
type Output = MathVector<<T as Add<U>>::Output>;
fn add(self, other: MathVector<U>) -> Self::Output {
self.assert_same_lenght(&other);
let output: Vec<_> = iter::zip(self.vec, other.vec).map(|(a, b)| a + b).collect();
MathVector::new(output)
}
}
which will cover all the possible Adds.
but whenever possible you should also provide operations by reference, to avoid needless cloning . here are what they would look like :
impl<'a, U, T: Add<&'a U>> Add<&'a MathVector<U>> for MathVector<T> {
type Output = MathVector<<T as Add<&'a U>>::Output>;
fn add(self, other: &'a MathVector<U>) -> Self::Output {
self.assert_same_lenght(&other);
// the & should be second to try and reuse the owned memory
let output = iter::zip(self.vec, &other.vec).map(|(a, b)| a + b).collect();
MathVector::new(output)
}
}
impl<'a, U, T> Add<MathVector<U>> for &'a MathVector<T>
where
&'a T : Add<U>
{
type Output = MathVector<<&'a T as Add<U>>::Output>;
fn add(self, other: MathVector<U>) -> Self::Output {
self.assert_same_lenght(&other);
// the & should be second to try and reuse the owned memory
let output = iter::zip(other.vec, &self.vec).map(|(b, a)| a + b).collect();
MathVector::new(output)
}
}
impl<'a, U, T> Add<&'a MathVector<U>> for &'a MathVector<T>
where
&'a T : Add<&'a U>
{
type Output = MathVector<<&'a T as Add<&'a U>>::Output>;
fn add(self, other: &'a MathVector<U>) -> Self::Output {
self.assert_same_lenght(&other);
let output = iter::zip(&self.vec, &other.vec).map(|(a, b)| a + b).collect();
MathVector::new(output)
}
}
this allows adding two &MathVector<i32>s together for example