Using nalgebra in generics

I have this code:

struct NPoint<N>(VectorN<usize, N>) where N : Dimension;

Where Dimension was defined as

trait Dimension: DimName + Dim {}

because of other errors in other generics.

The error I get at the struct definition is:

error[E0277]: the trait bound `<N as nalgebra::DimName>::Value: std::ops::Mul<typenum::UInt<typenum::UTerm, typenum::B1>>` is not satisfied
  --> src/lib.rs:24:18
   |
24 | struct NPoint<N>(VectorN<usize, N>) where N : Dimension;
   |                  ^^^^^^^^^^^^^^^^^^ no implementation for `<N as nalgebra::DimName>::Value * typenum::UInt<typenum::UTerm, typenum::B1>`
   |
   = help: the trait `std::ops::Mul<typenum::UInt<typenum::UTerm, typenum::B1>>` is not implemented for `<N as nalgebra::DimName>::Value`
   = help: consider adding a `where <N as nalgebra::DimName>::Value: std::ops::Mul<typenum::UInt<typenum::UTerm, typenum::B1>>` bound
   = note: required by `nalgebra::MatrixArray`

I have tried adding the help suggested by the compiler, but that just made it worse, I don’t even know what the error became, it just went on and on.

The docs said I could use typenum, so originally I tried to just use N : Unsigned in my generics, but I was getting errors about Dim and DimName, that’s why I defined Dimension. I tried looking at the definition of DimName but it’s too high level for me.

Full disclosure: I am pretty noobish at Rust.

I ended up in what seems to be the same situation when trying to have a matrix of generic size inside a struct:

extern crate nalgebra as na;
use na::{Scalar, Dim, MatrixN};

fn main() {}

struct Container<T: Scalar, D: Dim>
{
    x: MatrixN<T, D>
}

That gives me this error:

error[E0277]: the trait bound `D: na::DimName` is not satisfied
 --> src/main.rs:8:5
  |
8 |     x: MatrixN<T, D>
  |     ^^^^^^^^^^^^^^^^ the trait `na::DimName` is not implemented for `D`
  |
  = help: consider adding a `where D: na::DimName` bound
  = note: required by `na::MatrixArray`

Just like you, chasing down the path of adding trait bounds didn’t seem to lead anywhere. What am I missing? Isn’t this how you’re supposed to do it?

That kind of genericity is quite hard to use on the current version of nalgebra unfortunately. You might find that part of the documentation useful. All this will become much simpler when rust will support specialization of trait implementations…

In the mean time, the following should work for your examples:

extern crate nalgebra as na;
use na::{DefaultAllocator, Scalar, Dim, OwnedMatrix, OwnedColumnVector};
use na::dimension::U1;
use na::allocator::Allocator;

fn main() {}

struct NPoint<N: Dim>(OwnedColumnVector<usize, N, DefaultAllocator>)
    where DefaultAllocator: Allocator<usize, N, U1> ;

struct Container<T: Scalar, D: Dim>
    where DefaultAllocator: Allocator<T, D, D>
{
    x: OwnedMatrix<T, D, D, DefaultAllocator>
} 

In a near future release, this will be simplifiable to:

extern crate nalgebra as na;
use na::{DefaultAllocator, Scalar, Dim, VectorN, MatrixN};
use na::allocator::Allocator;

fn main() {}

struct NPoint<N: Dim>(VectorN<usize, N>)
    where DefaultAllocator: Allocator<usize, N> ;

struct Container<T: Scalar, D: Dim>
    where DefaultAllocator: Allocator<T, D, D>
{
    x: MatrixN<T, D>
}

Unfortunately, we can’t get rid of the where clauses without support of specialization.

1 Like

In version 0.14.0, the following works for dimensional genericity:

pub fn derive<N: Dim + DimName>(
        &mut self,
        t: f64,
        state: VectorN<f64, N>,
    ) -> (f64, VectorN<f64, N>)
    where
        DefaultAllocator: Allocator<f64, N>,
    {
     // ...
    }

P.S.: I slightly modified the code above to remove a Fn closure parameter, but I think this code is correct. With the Fn parameter, it compiles and the tests run.

Does this approach generalize to multiple arrays/matrices with different length?

I am currently trying to do something like

struct CombineStuff<F, DimX, DimY>
where
    F: Scalar,
    DimX: Dim + DimName,
    DimY: Dim + DimName,
{
    x: VectorN<F, DimX>,
    y: VectorN<F, DimY>,
    xy: MatrixMN<F, DimX, DimY>,
    yx: MatrixMN<F, DimY, DimX>,
}

But it seems that I would need multiple different DefaultAllocator definitions for this.

Hi @MichaelMauderer!

Yet, it generalises to multiple arrays/matrices. All you have to do is to add multiple Allocator bounds to the DefaultAllocator struct:

where DefaultAllocator: Allocator<F, DimX> 
                      + Allocator<F, DimY>
                      + Allocator<F, DimX, DimY>
                      + Allocator<F, DimY, DimX>

That’s a bit verbose but unfortunately necessary until Rust supports specialisation.

Also note that you don’t need to have both Dim + DimName. Only one of those is enough: use DimName if you want to allow only dimensions known at compile-time or use Dim if you want to allow both dimensions known at compile-time and Dynamic (dimensions only known at runtime).

1 Like