Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unconstrained type parameter error

Tags:

rust

glium

I'm trying to interface glium with cgmath. Following this answer, I have implemented a ToArray trait to convert instances of cgmath::Matrix4 into a format usable by glium:

pub trait ToArray {
    type Output;
    fn to_array(&self) -> Self::Output;
}

impl<S: cgmath::BaseNum> ToArray for cgmath::Matrix4<S> {
    type Output = [[S; 4]; 4];
    fn to_array(&self) -> Self::Output {
        (*self).into()
    }
}

Since I don't always use Matrix4 directly, I need a similar implementation for cgmath transform types. For example for cgmath::Decomposed:

impl<S: cgmath::BaseFloat, R: cgmath::Rotation3<S>> ToArray
    for cgmath::Decomposed<cgmath::Vector3<S>, R> {
    type Output = [[S; 4]; 4];
    fn to_array(&self) -> Self::Output {
        cgmath::Matrix4::<S>::from(*self).into()
    }
}

This works, but I'd like to avoid duplicating the code for all transform types, so I thought I would define a generic implementation for anything that can be converted to a Matrix4:

impl<S: cgmath::BaseFloat, T: Into<cgmath::Matrix4<S>>> ToArray for T {
    type Output = [[S; 4]; 4];
    fn to_array(&self) -> Self::Output {
        cgmath::Matrix4::<S>::from(*self).into()
    }
}

Unfortunately, this doesn't work:

error[E0207]: the type parameter `S` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:23:6
   |
23 | impl<S: cgmath::BaseFloat, T: Into<cgmath::Matrix4<S>>> ToArray for T {
   |      ^ unconstrained type parameter

I have two questions:

  • Why doesn't the above code work? From reading the rustc --explain output, I would expect the T: Into<cgmath::Matrix4<S>> to act as a valid constraint on S as well as T.
  • How can I write a generic implementation for anything that can be converted into a Matrix4?
like image 299
Jmb Avatar asked May 12 '17 08:05

Jmb


1 Answers

Imagine I defined a type like this1:

struct PolymorphicMatrix;

impl Into<cgmath::Matrix4<f32>> for PolymorphicMatrix {
    fn into(self) -> cgmath::Matrix4<f32> {
        cgmath::Matrix4::new(
            1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0,
            1.0, 1.0, 1.0, 1.0)
    }
}

impl Into<cgmath::Matrix4<f64>> for PolymorphicMatrix {
    fn into(self) -> cgmath::Matrix4<f64> {
        cgmath::Matrix4::new(
            2.0, 2.0, 2.0, 2.0,
            2.0, 2.0, 2.0, 2.0,
            2.0, 2.0, 2.0, 2.0,
            2.0, 2.0, 2.0, 2.0)
    }
}

Which of these impls will be used to implement ToArray? Both are applicable, but you can only implement ToArray once for PolymorphicMatrix, because ToArray has no type parameters. That's what the error means: it's not valid because it would cause issues in a situation like this.

Since you control neither Into nor cgmath::Matrix4, the only aspect you can change is ToArray. You can add a type parameter that is not used in the trait definition itself, and implementations can use that type parameter.

pub trait ToArray<S> {
    type Output;
    fn to_array(&self) -> Self::Output;
}

impl<S: cgmath::BaseFloat, T: Into<cgmath::Matrix4<S>>> ToArray<S> for T {
    type Output = [[S; 4]; 4];
    fn to_array(&self) -> Self::Output {
        cgmath::Matrix4::<S>::from(*self).into()
    }
}

Naturally, you can't enforce any kind of correlation between S and Output. Also, that type parameter might cause some ambiguities: since it's not used in the trait, the compiler might not be able to infer S from usage in some situations, so you may have to specify it explicitly. If that becomes a problem, you might want to explore using generic-array. It would let you lift the array dimensions to type parameters, so that you could get rid of the associated type and instead use the type parameters directly in the return type of to_array, which would help the compiler's inference.


1 Normally, one would implement From rather than Into. I'm using Into here to stay closer to the problem as stated.

like image 138
Francis Gagné Avatar answered Oct 08 '22 15:10

Francis Gagné