Generic hell implementation quilt

I do Rust, implementing matrix math, and I come across several snags. I identified features that I thought were relevant to the matrix.

trait Matrix<T> where T : num::Num { fn dim(&self) -> (usize, usize); fn elem(&self, i : usize, j : usize) -> Option<& T>; fn new_const(v: T, rows : usize, cols : usize) -> Self where T : Clone; fn same_dim<U>(&self, other: &U) -> bool where U : Matrix<T> { self.dim() == other.dim() } } 

I have a dumb implementation using Vec<Vec<T>> . I implemented all the methods and tested them. They all work. Now I just want to add two matrices together. Therefore, without adding the row iterator, which, as I know, will be required, and performing the implementation of the addition, which, as I know, will be incorrect, I added the following.

 impl <T, U> Add for U where T: num::Num, U: Matrix<T> { type Output = U; fn add(self, _rhs: U) -> U { U::new_const(T::zero(), 5, 5) } } 

but i get

 lib.rs:41:7: 41:8 error: the type parameter `T` is not constrained by the impl trait, self type, or predicates [E0207] lib.rs:41 impl <T, U> Add for U where T: num::Num, U: Matrix<T> { ^ lib.rs:41:7: 41:8 help: run `rustc --explain E0207` to see a detailed explanation error: aborting due to previous error Could not compile `matrix`. To learn more, run the command again with --verbose. Compilation failed. 

T seems to me limited. Can someone point me in the right direction?

+6
source share
1 answer

T not limited to the information that the compiler can see on the implementation site. Having trait Matrix<T> , one type can be a matrix for several types of elements, that is, it is completely legal for someone to have both impl Matrix<u8> for Foo and impl Matrix<u16> for Foo in the same program. If this happens, then Foo + Foo (i.e., using the Add implementation) cannot be sure which T use: both T = u8 and T = u16 work.

I think the best way to solve this is to remove a parameter of type T : this type is only a matrix over one type of element (even if it is shared), for example. Vec<Vec<T>> is a matrix over T , nothing more. This is similar to an Iterator : a given type can only give one element type.

In code, it looks like this:

 trait Matrix { type Elem: num::Num; fn dim(&self) -> (usize, usize); fn elem(&self, i: usize, j: usize) -> Option<&Self::Elem>; // ... } 

Then the Add implementation becomes

 impl<U> Add for U where U: Matrix { type Output = U; fn add(self, _rhs: U) -> U { U::new_const(U::Elem::zero(), 5, 5) } } 

However, this also does not work, as it addresses issues of consistency. The compiler cannot be sure that this Add implementation does not overlap with other Add implementations, since it is possible to implement Matrix for a type that already has an Add implementation. You can get around this by creating a wrapper type, and instead use the Matrix attribute as the "backup storage" attribute, i.e. "Simple" controls the internal view.

 struct Matrix<T: Storage> { x: T } trait Storage { // renamed `Matrix` trait type Elem; fn dim(&self) -> (usize, usize); // ... } 

Then the Matrix type becomes a place to add additional methods and implementations of features, etc.

+8
source

All Articles