Bojangles
Bojangles

Reputation: 101513

Error when deriving Copy on a struct containing nalgebra's VectorN type

I'm attempting to use nalgebra's VectorN type to implement some dimension-agnostic calculations, but I'm getting some odd errors around the Copy trait. The below contrived test case demonstrates the problem:

extern crate nalgebra;

use nalgebra::allocator::Allocator;
use nalgebra::{DefaultAllocator, DimName, Real, VectorN};

#[derive(Clone, Debug, Copy, PartialEq)]
pub struct LinearPathSegment<N: Real, D: DimName>
where
    DefaultAllocator: Allocator<N, D>,
{
    pub some_vec: VectorN<N, D>,
    pub some_scalar: N,
}

Clone and run cargo build in this repo to reproduce

The error output by the compiler (rustc 1.29.1 (b801ae664 2018-09-20)) is this:

error[E0204]: the trait `Copy` may not be implemented for this type
  --> src/lib.rs:6:24
   |
6  | #[derive(Clone, Debug, Copy, PartialEq)]
   |                        ^^^^
...
11 |     pub some_vec: VectorN<N, D>,
   |     --------------------------- this field does not implement `Copy`

I'm certain that VectorN does implement Copy; by following the type aliases through the chain VectorN<...> -> MatrixMN<...> -> Matrix<...> we see that Copy is #[derive()]d on Matrix, which should mean it is derived for VectorN too. Why is the compiler saying otherwise? What do I need to do to make VectorN copyable?

Upvotes: 4

Views: 273

Answers (2)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65822

You must add the bound Owned<N, D>: Copy. Owned is used as part of the MatrixMN type alias. Owned ends up being a type alias for MatrixArray.

extern crate nalgebra;

use nalgebra::{DefaultAllocator, DimName, Real, VectorN};
use nalgebra::allocator::Allocator;
use nalgebra::storage::Owned;

#[derive(Clone, Debug, Copy, PartialEq)]
pub struct LinearPathSegment<N: Real, D: DimName>
where
    DefaultAllocator: Allocator<N, D>,
    Owned<N, D>: Copy,
{
    pub some_vec: VectorN<N, D>,
    pub some_scalar: N,
}

Real requires Copy, and DimName requires Dim which requires Copy, so N and D don't need to have an explicit Copy bound. But for some reason, the compiler is unable to prove that MatrixArray is Copy. I suspect this comes from the bound GenericArray<N, Prod<R::Value, C::Value>>: Copy in its Copy implementation.

Another option is to add the bound VectorN<N, D>: Copy.

Note that either option forces every use of your struct to meet that bound. If that's not what you want, you must write a manual impl for Copy with the appropriate bounds instead of deriving it.

impl<N, D> Copy for LinearPathSegment<N, D>
where
    N: Real,
    D: DimName,
    DefaultAllocator: Allocator<N, D>,
    VectorN<N, D>: Copy,
{
}

Upvotes: 4

Boiethios
Boiethios

Reputation: 42829

Deriving Copy is special in this case, because Matrix is generic:

#[repr(C)]
#[derive(Hash, Clone, Copy)]
pub struct Matrix<N: Scalar, R: Dim, C: Dim, S> {
    /// The data storage that contains all the matrix components and informations about its number
    /// of rows and column (if needed).
    pub data: S,

    _phantoms: PhantomData<(N, R, C)>,
}

The marker Copy is implemented only if N, R, C and S are also Copy.

Indeed, you can see this line in the Matrix documentation page:

impl<N: Copy + Scalar, R: Copy + Dim, C: Copy + Dim, S: Copy> Copy for Matrix<N, R, C, S>

As a simpler example, this code compiles fine:

#[derive(Clone, Copy)]
struct DummyWrapper<T>(T);

fn main() {
}

But of course, you can copy DummyWrapper only if T is copyable too:

#[derive(Clone, Copy)]
struct DummyWrapper<T>(T);

fn consume<T>(_t: T) {}

fn main() {
    // Ok
    let a = DummyWrapper(42);
    consume(a);
    consume(a);

    // Error: move occurs because `a` has type `DummyWrapper<std::string::String>`,
    // which does not implement the `Copy` trait
    let a = DummyWrapper(String::from("test"));
    consume(a);
    consume(a);
}

To make your struct copyable, I am only guessing, but you should add the following bounds:

N: Copy, D: Copy

Upvotes: 0

Related Questions