unconstrained type parameter error

glium, rust

Solution

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.

Problem

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`?

Original source

Related problems