Associated constants error in Matrix library


#1

Hi there. :slightly_smiling_face: I’m working on a small library that does some matrix math and decided to make it more generic. Instead of having a Matrix4x4 type, I was hoping to build a Matrix<M, N> type and use associated constants to create the underlying array. It seemed like this should be straight forward, but I have not been able to make the compiler happy.

I tried a handful of permutations, but the errors are the same and make me think I’ve run into an edge case not supported by the compiler–or I’ve completely misunderstood something (more likely). Any insight into what I could do here?

Thanks!

pub trait Size {
    const VALUE: usize; // Two warnings here...
}

pub struct One;
pub struct Two;
pub struct Three;
pub struct Four;

impl Size for One   { const VALUE: usize = 1; }
impl Size for Two   { const VALUE: usize = 2; }
impl Size for Three { const VALUE: usize = 3; }
impl Size for Four  { const VALUE: usize = 4; }

pub struct Matrix<M: Size, N: Size> {
    matrix: [f32; <M as Size>::VALUE * <N as Size>::VALUE], // Two errors here
    m: std::marker::PhantomData<M>,
    n: std::marker::PhantomData<N>,
}

impl<M: Size, N: Size> Matrix<M, N> {
    pub fn is_square(&self) -> bool {
        // No errors, this works
        <M as Size>::VALUE == <N as Size>::VALUE
    }
    
    pub fn size(&self) -> f32 {
        // No errors, this works
        <M as Size>::VALUE * <N as Size>::VALUE
    }
    
    pub fn new() -> Self {
        // No errors, this works
        let matrix = [0; <M as Size>::VALUE * <N as Size>::VALUE];
        Self {
            matrix,
            m: std::marker::PhantomData,
            n: std::marker::PhantomData,
        }
    }
}

impl<M: Size> Matrix<M, M> {
    fn det(&self) -> f32 {
        42.
    }
}

#[inline]
pub fn new_zero<M: Size, N: Size>() -> Matrix<M, N> {
    // This function appears to work...
    let matrix = [0; <M as Size>::VALUE * <N as Size>::VALUE];
    Matrix {
        matrix,
        m: std::marker::PhantomData,
        n: std::marker::PhantomData,
    }
}

type Matrix3x4 = Matrix<Three, Four>;

fn main() {
    let matrix4x4 = new_zero::<Four, Four>();
    let det = matrix4x4.det();
    assert_eq!(det, 42.);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `M: Size` is not satisfied
  --> src/main.rs:16:19
   |
16 |     matrix: [f32; <M as Size>::VALUE * <N as Size>::VALUE], // Two errors here
   |                   ^^^^^^^^^^^^^^^^^^ the trait `Size` is not implemented for `M`
   |
   = help: consider adding a `where M: Size` bound
note: required by `Size::VALUE`
  --> src/main.rs:2:5
   |
2  |     const VALUE: usize; // Two warnings here...
   |     ^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `N: Size` is not satisfied
  --> src/main.rs:16:40
   |
16 |     matrix: [f32; <M as Size>::VALUE * <N as Size>::VALUE], // Two errors here
   |                                        ^^^^^^^^^^^^^^^^^^ the trait `Size` is not implemented for `N`
   |
   = help: consider adding a `where N: Size` bound
note: required by `Size::VALUE`
  --> src/main.rs:2:5
   |
2  |     const VALUE: usize; // Two warnings here...
   |     ^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.


#2

Rust doesn’t have const generics yet, so the thing you’re trying to do is going to run into lots of limitations.

Check out the typenum crate for the currently possible workarounds.

If you’re OK having just certain specific sizes, I suggest keeping the code non-generic and/or using macros to generate desired sizes until const generics are available in the language.


#3

Perfect, thanks so much! I figured it was going to be something like this. I’m looking forward to seeing const generics added–but I’ll figure it out another way for now. typenum looks interesting and I’ll see if I can get some inspiration that.

I appreciate the quick response!