Home > OS >  using const generic in implementation of trait for another trait causes "unconstrained const pa
using const generic in implementation of trait for another trait causes "unconstrained const pa

Time:11-28

I'm trying to implement some abstract linear algebra traits and structs as part of my practices learning rust. In the code below when I implement VectorAbstract<TValue> for VectorDynamic<TValue> there's no problem; but when I try to do the same for VectorStatic<TValue, DIM_COUNT> I get the following error :

the const parameter `DIM_COUNT` is not constrained by the impl trait, self type, or predicates
unconstrained const parameter
note: expressions using a const parameter must map each value to a distinct output value
note: proving the result of expressions other than the parameter are unique is not supported

What am I doing wrong here ?
What's the correct way to implement this ?
Here's my code :

pub trait VectorAbstract<TValue: Scalar>: VectorSpaceElement   // a dozen std traits
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue;
}

pub trait VectorDynamic<TValue: Scalar>: VectorAbstract<TValue> {
    fn dim_count(&self) -> usize;
    fn from_list(arr: Vec<TValue>) -> Self;
}

pub trait VectorStatic<TValue: Scalar, const DIM_COUNT: usize>: VectorAbstract<TValue> {
    fn from_array(arr: [TValue; DIM_COUNT]) -> Self;
}

impl<TValue: Scalar, TVector: VectorDynamic<TValue>> VectorAbstract<TValue> for TVector {
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        // ... dot product body for dynamic vectors ...
    }
}

impl<TValue: Scalar, const DIM_COUNT: usize, TVector: VectorStatic<TValue, DIM_COUNT>>
    VectorAbstract<TValue> for TVector
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        // ... dot product body for static vectors ...
    }
}

UPDATE 1

I also tried the following :

pub trait VectorStatic<TValue: Scalar>: VectorAbstract<TValue> {
    const DIM_COUNT: usize;
    fn from_array(arr: [TValue; Self::DIM_COUNT]) -> Self;
}
impl<TValue: Scalar, TVector: VectorStatic<TValue>> VectorAbstract<TValue> for TVector {
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        // ... dot product body for static vectors ...
    }
}

But I get the following error where I'm declaring the from_array function :

generic parameters may not be used in const operations  
cannot perform const operation using `Self`  
note: type parameters may not be used in const expressions  

Note : I want the DIM_COUNT to be obtained in compile time somehow, so no fn dim_count() for VectorStatic.

UPDATE 2

Also when I completely remove the implementation for VectorStatic, I get another error in another file where I have a Vector2 struct and try to implement VectorStatic<TValue, 2> for that:

pub struct Vector2<TValue: Scalar> {
    pub x: TValue,
    pub y: TValue,
}
impl<TValue: Scalar> VectorStatic<TValue, 2> for Vector2<TValue> {
    // ... implementation ...
}

And here's the error :

the trait bound `math::algebra::vectors::vector2::Vector2<TValue>: math::algebra::abstractions::VectorDynamic<TValue>` is not satisfied
the trait `math::algebra::abstractions::VectorDynamic<TValue>` is not implemented for `math::algebra::vectors::vector2::Vector2<TValue>`

Note that this Vector2 struct is not supposed to be a VectorDynamic at all. There's definitely something I have not understood clearly here, maybe rust thinks I'm trying to make every VectorAbstract a VectorDynamic or something like that ?
What is it that I'm missing ?

UPDATE 3

Perhaps I should also mention that I already tried the following :

impl<TValue: Scalar> VectorAbstract<TValue> for dyn VectorDynamic<TValue>
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        // ... dot product body for dynamic vectors ...
    }
}
impl<TValue: Scalar, const DIM_COUNT: usize> VectorAbstract<TValue> for dyn VectorStatic<TValue, DIM_COUNT>
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        // ... dot product body for static vectors ...
    }
}

But in that case I get this error for both :

the trait `math::algebra::abstractions::VectorDynamic/VectorStatic` cannot be made into an object

and then a dozen lines that say because they used Self as a type parameter.

CodePudding user response:

The problem with your original code is that there are no guarantees that a type TVector will only implement VectorStatic<TValue, 1> and not implement VectorStatic<TValue, 2> which would give conflicting implementations of VectorAbstract<TValue>.

Update 1 is the way to go, since that way a type cannot implement VectorStatic more than once. Currently this requires nightly rust with #![feature(generic_const_exprs)] added at the start of lib.rs/main.rs

The next problem will be the 2 impl-s of VectorAbstract<TValue>. Again there is nothing to guarantee that TVector does not implement both VectorDynamic<TValue> and VectorStatic<TValue> which would give conflicting implementations of VectorAbstarct<TValue>. As far as I know this cannot be done even with unstable features. The best way do it is to use wrapper structs.

VectorStatic1 VectorStaticWrapper1 requires the added feature and works only in nightly rust version, VectorStatic2 VectorStaticWrapper2 works in stable.

#![feature(generic_const_exprs)]

pub trait Scalar {}

pub trait VectorAbstract<TValue: Scalar>: 
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue;
}

pub trait VectorDynamic<TValue: Scalar>: VectorAbstract<TValue> {
    fn dim_count(&self) -> usize;
    fn from_list(arr: Vec<TValue>) -> Self;
}

pub trait VectorStatic1<TValue: Scalar>: VectorAbstract<TValue> {
    const DIM_COUNT: usize;
    fn from_array(arr: [TValue; Self::DIM_COUNT]) -> Self;
}

pub trait VectorStatic2<TValue: Scalar, const DIM_COUNT: usize>: VectorAbstract<TValue> {
    fn from_array(arr: [TValue; DIM_COUNT]) -> Self;
}

struct VectorDynamicWrapper<T>(T);
struct VectorStaticWrapper1<T>(T);
struct VectorStaticWrapper2<T, const DIM_COUNT: usize>(T);

impl<TValue: Scalar, TVector: VectorDynamic<TValue>> VectorAbstract<TValue> for VectorDynamicWrapper<TVector> {
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        let lhs = &lhs.0;
        let rhs = &rhs.0;
        todo!()
    }
}

impl<TValue: Scalar, TVector: VectorStatic1<TValue>>
    VectorAbstract<TValue> for VectorStaticWrapper1<TVector>
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        let lhs = &lhs.0;
        let rhs = &rhs.0;
        let dim_count = TVector::DIM_COUNT;
        todo!()
    }
}

impl<TValue: Scalar, const DIM_COUNT: usize, TVector: VectorStatic2<TValue, DIM_COUNT>>
    VectorAbstract<TValue> for VectorStaticWrapper2<TVector, DIM_COUNT>
{
    fn dot(lhs: &Self, rhs: &Self) -> TValue {
        let lhs = &lhs.0;
        let rhs = &rhs.0;
        todo!()
    }
}
  • Related