PartialEq error when change const generics

I'm trying to write the PartialEq trait for a struct and emits this error:

conflicting implementations of trait `PartialEq` for type `Matrix<_, _>`

where there are two different implementations (at least with alternated const generics):

impl<const M: usize, const N: usize> PartialEq for Matrix<M, N>
where
    [f64; M * N]:,
{ ... }

impl<const M: usize, const N: usize> PartialEq<Matrix<N, M>> for Matrix<M, N>
where
    [f64; N * M]:,
    [f64; M * N]:,
{ ... }

My solution on this is trying to tell to the compiler that in some scenarios if M == N then M is N and can be also "partialequalized" with a Matrix<N, M>:

/// &self as Matrix<M, N>
pub fn reverse_types(&self) -> Option<Matrix<N, M>>
    where
        [f64; M * N]:,
        [f64; N * M]:,
    {
        if !self.is_square() {
            return None;
        }

        let mut data: [f64; N * M] = [0.; N * M];
        data.par_iter_mut()
            .enumerate()
            .for_each(|(idx, el)| *el = self.data[idx]);

        Some(Matrix {
            rows: N,
            cols: M,
            data,
        })
    }

But i don't like this runtime solution when all the checks are at compile time.

The target use case is this:

/// &self as Matrix<M, N>
pub fn is_symmetric(&self) -> bool
    where
        [f64; N * M]:,
    {
        if !self.is_square() {
            return false;
        }
        
        /// self.transpose() as Matrix<N, M>
        let Some(transposed) = self.transpose().reverse_types() else {
            return false;
        };

        *self == transposed
    }

Just remove the first impl. The second one implies it (with the choices of M' == N, N' == M).

If i remove the first impl then i have errors because the second doesn't implies it and expects equality of reversed generics only:

mismatched types
expected struct `matrix::Matrix<4, 2>`
   found struct `matrix::Matrix<2, 4>`

#[test]
    fn matrix_can_set_row() {
        #[rustfmt::skip]
        let mut matrix = Matrix::<2, 4>::from([
            0., 0., 0., 0.,
            0., 0., 0., 0.,
        ]);

        matrix.set_row(1, [2., 0., 0., 0.]).unwrap();
        matrix.set_row(2, [1., 1., 1., 1.]).unwrap();

        #[rustfmt::skip]
        assert_eq!(matrix, Matrix::<2, 4>::from([ // <-- HERE
            2., 0., 0., 0.,
            1., 1., 1., 1.,
        ]));
    }

Ah, well, yes, the latter impl is PartialEq<Matrix<N, M>> for Matrix<M, N> and not PartialEq<Matrix<N, M>> for Matrix<K, L>.

Thinking of it, you should actually remove the second impl. The first impl will trivially cover the case M == N.

Yeah, this was my first thought but for Rust compiler doesn't trivially cover this because M == N && N == M but M is not N and N is not M. They are different generics that represents the same value (in this case).

mismatched types
expected struct `Matrix<M, N>`
   found struct `Matrix<N, M>`

pub fn is_symmetric(&self) -> bool
    where
        [f64; N * M]:,
    {
        if !self.is_square() {
            return false;
        }

        *self == self.transpose() /// <-- HERE
    }

The problem is that when I implement PartialEq<Matrix<N, M>> for Matrix<M, N>, it tells me that I cannot implement it because it conflicts with the first implementation, necessary to be able to make equivalences between equal matrices. But when I apply those same equivalences with transposed matrices, it is able to recognize that Matrix<M, N> and Matrix<N, M> are not the same.

For the implementation of the trait they are the same matrix but when applying the equivalence they are not the same.

If you want to express that they are the same, why not actually make them the same? This is trivial and works perfectly fine, and it only makes sense to have the operation on square matrices anyway:

impl<const N: usize, T: PartialEq> Matrix<N, N, T> {
    fn is_symmetric(&self) -> bool {
        *self == self.transpose()
    }
}
2 Likes

Yes, this is the way. Thanks!

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.