Nalgebra - Error multiplying matrix by vector in generic size struct

Running into what seems like an odd issue when trying to multiply a square matrix A with X rows and X columns by a vector x with X rows, constructed from a struct with generic sizing. Just a basic A*x = b operation. The end goal is to have a struct type with a generic size X that can be declared when the struct is created, rather than use a static sizing.

The data struct and associated methods are as follows:

struct Foo<T, const X: usize>{
    A: na::base::SMatrix<T, X, X>,
    x: na::base::SVector<T, X>
}

impl<T, const X: usize> Foo<T, X> {
    fn b(&self) -> na::base::SVector<T, X> {
        self.A*self.x
    }
}

However, the compiler throws an error when trying to multiply the two, that A and x are incompatible:

error[E0369]: cannot multiply `Matrix<T, Const<X>, Const<X>, ArrayStorage<T, X, X>>` by `Matrix<T, Const<X>, Const<1_usize>, ArrayStorage<T, X, 1_usize>>`
   --> src\main.rs:989:15
    |
989 |         self.A*self.x
    |         ------^------ Matrix<T, Const<X>, Const<1_usize>, ArrayStorage<T, X, 1_usize>>
    |         |
    |         Matrix<T, Const<X>, Const<X>, ArrayStorage<T, X, X>>

What am I doing wrong here? I'm still learning Rust's generic type system, is there something in the impl declaration I should change? Would appreciate any feedback on implementation.

Whew the Matrix docs page is huge huh?

The normal way to deal with these situations is to constrain T so you can perform all the operations you need without being overly specific about how you're going to use it. Unfortunately doing that here is both verbose and fragile, since you have to add a dependency on num-traits and make sure you keep the version matched to what nalgebra uses.

Heres what that looks like.

Playground

use nalgebra::base::{SMatrix, SVector};
use std::fmt::Debug;

struct Foo<T, const X: usize> {
    A: SMatrix<T, X, X>,
    x: SVector<T, X>,
}

impl<T, const X: usize> Foo<T, X>
where
    T: std::ops::MulAssign
        + std::ops::AddAssign
        + PartialEq
        + Debug
        + Clone
        + num_traits::identities::One
        + num_traits::Zero
        + 'static,
{
    fn b(&self) -> SVector<T, X> {
        &self.A * &self.x
    }
}

fn main() {
    let foo = Foo {
        A: SMatrix::<f32, 2, 2>::new(1., 2., 3., 4.),
        x: SVector::<f32, 2>::new(5., 6.),
    };

    println!("{:?}", foo.b());
}

The other option is to explicitly ask for conformance to the trait that will let you do what you want, which doesn't involve the num-traits dependency, but does require a Higher Ranked Trait Bound.

Playground

use nalgebra::base::{SMatrix, SVector};
use std::ops::Mul;

struct Foo<T, const X: usize> {
    A: SMatrix<T, X, X>,
    x: SVector<T, X>,
}

impl<T, const X: usize> Foo<T, X>
where
    for<'a> &'a SMatrix<T, X, X>: Mul<&'a SVector<T, X>, Output = SVector<T, X>>,
{
    fn b(&self) -> SVector<T, X> {
        &self.A * &self.x
    }
}

fn main() {
    let foo = Foo {
        A: SMatrix::<f32, 2, 2>::new(1., 2., 3., 4.),
        x: SVector::<f32, 2>::new(5., 6.),
    };

    println!("{:?}", foo.b());
}