Need help with fixing code duplication

I have my own Matrix implementation and I want to avoid type code duplication (related to numeric traits).
This is my Matrix:

#[derive(Debug, Clone, Default)]
pub struct Matrix<N> {
    // ...
    data: Vec<N>,
}

impl<N> Matrix<N> where
    N: Default
    + Debug
    + Clone
    + Copy
    + PartialOrd
    + Div<Output = N>
    + Add<Output = N>
    + Sub<Output = N>
    + Mul<Output = N>
    + SampleUniform
    + Neg<Output = N>
    + AddAssign
    + SubAssign {
    // ...
}

So, in case I impl extra trait: Add, Sub, Neg etc I need to copy all types from above and add into all that traits:

impl <N> Neg for Matrix<N> where
    N: Div<Output = N>
    + Add<Output = N>
    + Sub<Output = N>
    + Mul<Output = N>
    + Default
    + Debug
    + PartialOrd
    + SampleUniform
    + Clone
    + Copy
    + Neg<Output = N>
    + AddAssign
    + SubAssign {
    type Output = Matrix<N>;

    fn neg(self) -> Self::Output {
        // ...
    }
}

impl<N> SubAssign<N> for Matrix<N> where
    N: Div<Output = N>
    + Add<Output = N>
    + Sub<Output = N>
    + Mul<Output = N>
    + Default
    + Debug
    + PartialOrd
    + SampleUniform
    + Clone
    + Copy
    + Neg<Output = N>
    + AddAssign
    + SubAssign {
    fn sub_assign(&mut self, scalar: N) {
        // ...
    }
}

I am not sure how to move this into type to allow generics, could somebody help on that ?

1 Like

Most of the traits probably don't need the whole list. Matrix<N> can probably implement Neg for all N:Neg, for example, even if the result can't be used for some of the other operations.

Alternatively, you can define a custom trait with a blanket implementation, and then use that bound everywhere:

trait MatrixElement:
    Div<Output=Self> + 
    Add<Output=Self> +
    // ...
    SubAssign
{}

impl<T> MatrixElement for T where T:
    Div<Output=Self> + 
    Add<Output=Self> +
    // ...
    SubAssign
{}

impl<N> Matrix<N> where N:MatrixElement { ... }
impl<N> SubAssign for Matrix<N> where N:MatrixElement { ... }
// ...
    
6 Likes

Also, consider using some of the comprehensive traits from num-traits.

3 Likes

Unfortunately, this approach not works for me.
I added an example which works for me as expected

And example with approach you suggested. Could you please review ?

You didn't pay attention to the suggested solution. The blanket impl is missing. This compiles.

Requiring supertrait bounds doesn't automatically generate blanket impls for all possible types. (That wouldn't even be possible for traits with methods, but even trivial marker traits shouldn't just be implemented by the compiler willy-nilly — some types must not implement some traits, and so the only right design is to require manual impls.)

4 Likes

Thank you for the detailed explanation !

Could you please also help on another one part: I need to impl math for all numeric types to allow actions on them using matrix. For example:

let matrix: f64 = Matrix::new(3, 3).randomize(0., 1.);
let num: f64 = 1.;
let result = 1. + matrix;

for this purpose I do smth like this:

impl Add<Matrix<f64>> for f64 {
    type Output = Matrix<f64>;

    fn add(self, mut matrix: Matrix<f64>) -> Self::Output {
        matrix.assign(matrix.data.iter().map(|&item| item + self).collect()).unwrap();

        matrix
    }
}

impl Add<Matrix<f32>> for f32 {
    type Output = Matrix<f32>;

    fn add(self, mut matrix: Matrix<f32>) -> Self::Output {
        matrix.assign(matrix.data.iter().map(|&item| item + self).collect()).unwrap();

        matrix
    }
}

but it seems to be too much code to repeat myself for every numeric type. Does it possible to make it shorter ?

That seems like it should be a similar blanket impl for T: Add.

Seems like it's not correct syntax:

impl<T> Numeric<T> for Add<T> {}

Could you point me in right direction ?

That is correct syntax, the problem is not the syntax but the fact that you don't want to impl Numeric, do you? Based on your example, you want to impl<T: Numeric> Add<Matrix<T>> for T.

1 Like

Now I got this error:

type parameter `T` must be covered by another type when it appears before the first local type (`Matrix<T>`)

The updated example is here.

Well, in this case you can't have a blanket impl. (Your link is broken.)

1 Like

I updated the link, could you please check ?

The compiler error message is pretty clear — you can't have that impl. You'll have to implement it for concrete types (perhaps use a macro for that).

1 Like

Thank you for the hint. Finally I use this solution:

macro_rules! impl_matrix_ops_for_scalar {
    ($($T:ty),*) => {
        $(
            impl Add<Matrix<$T>> for $T {
                type Output = Matrix<$T>;

                fn add(self, mut matrix: Matrix<$T>) -> Self::Output {
                    matrix.assign(matrix.data.iter().map(|&item| item + self).collect()).unwrap();

                    matrix
                }
            }

            impl Sub<Matrix<$T>> for $T {
                type Output = Matrix<$T>;

                fn sub(self, mut matrix: Matrix<$T>) -> Self::Output {
                    matrix.assign(matrix.data.iter().map(|&item| self - item).collect()).unwrap();

                    matrix
                }
            }

            impl Mul<Matrix<$T>> for $T {
                type Output = Matrix<$T>;

                fn mul(self, mut matrix: Matrix<$T>) -> Self::Output {
                    matrix.assign(matrix.data.iter().map(|&item| item * self).collect()).unwrap();

                    matrix
                }
            }

            impl Div<Matrix<$T>> for $T {
                type Output = Matrix<$T>;

                fn div(self, mut matrix: Matrix<$T>) -> Self::Output {
                    matrix.assign(matrix.data.iter().map(|&item| self / item).collect()).unwrap();

                    matrix
                }
            }
        )*
    };
}

impl_matrix_ops_for_scalar!(f64, f32, i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, usize);

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.