You generally only need bounds on generics. When you're working with concrete types, you don't have to state the bounds they implement in order to make use of them. That's why you can print a String
without a where String: Display
on your function, add literal integers, etc.
(And stating bounds that aren't true won't make them come true either.)
So I removed those bounds:
impl Submatrix<4, 4, f32> for Matrix<4, 4, f32> {
type Result = Matrix<3, 3, f32>;
}
And that compiles, but the the bounds on the submatrix
method doesn't compile if you try to use it:
76 | m4.submatrix(0, 0)
| ^^^^^^^^^ the trait `for<'a> From<&'a f32>` is not implemented for `f32`
...
--> src/lib.rs:55:13
|
51 | fn submatrix(&self, skiprow: usize, skipcol: usize) -> Self::Result
| --------- required by a bound in this
...
55 | for<'a> From<&'a <Self as Index<(usize, usize)>>::Output>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Submatrix::submatrix`
I haven't deep-dived on understanding this code, but offhand it looks like your T
are generally simple, Default
-able, maybe even always Copy
-able types. So I tweaked things a bit:
pub trait FromOtherOutput<Other: ?Sized>
where
// No longer `for<'any> From<&Other::Output>`
Self: From<Other::Output>,
Other: Index<(usize, usize)>,
// Added and we have to repeat it everywhere
Other::Output: Clone,
{}
// ... replaced bounds everywhere...
// ... added `: Clone` where needed...
// ... and in `fn submatrix`:
let tmp = &self[(r, c)];
acc[(ir, ic)] = tmp.clone().into();
And now that compiles.
However, I don't think it's really an ideal construction. Now we have another bound that's not implied and has to be carried around everywhere. Also, is the real requirement that everything uses the same underlying T
? Do you have a concrete use-case where you need to be this generic?
Because it feels like a lot of heavy machinery for maybe being able to get a 3x3 f32
submatrix out of a 4x4 f64
matrix without an intermediate 3x3 f64
submatrix, so far. (Or maybe a whole lot of machinery for nothing, if there's no demand for such operations.)
I.e., is there a reason this doesn't work for you?
pub trait Submatrix<const ROW: usize, const COL: usize, T>
where
T: Clone,
Self: Index<(usize, usize), Output = T>,
{
type Result: Default + IndexMut<(usize, usize), Output = T>;
fn submatrix(&self, skiprow: usize, skipcol: usize) -> Self::Result {
iproduct!(0..ROW, 0..COL)
.filter(|(r, c)| *r != skiprow && *c != skipcol)
.enumerate()
.fold(Self::Result::default(), |mut acc, (i, (r, c))| {
let ir = i / (COL - 1);
let ic = i % (COL - 1);
acc[(ir, ic)] = self[(r, c)].clone();
acc
})
}
}