Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

"Unconstrained Type" error if associated type is also parameter

I’m beating my head on a complex type problem, and running into issue with how type constraints work.

I want to have something basically like this:

trait TraitWithParameter<T>{}
trait AnotherTrait{ fn implemented(&self) {println!("Implemented and called")}}
impl<T> AnotherTrait for TraitWithParameter<T>{}

But that doesn’t work because it requires me to declare it as dyn TraitWithParameter<T>{}, and that doesn’t seem to actually convince rust that types I want implement the trait unless I jump through some hoops to declare them as such.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

The compiler recommends making that into:

impl<T, U> AnotherTrait for T where T: TraitWithParameter<U>{}

Except if I do that, the compiler complains that U is unconstrained. Reading up on that error, I find it does work to do:

trait TraitWithAssociatedType{
    type AssociatedType;
}
impl<T, U> AnotherTrait for T where T: TraitWithAssociatedType<AssociatedType = U>{}

I don’t understand why that works and the prior version doesn’t, but more importantly I need to have a parameter on the base trait. (This is because otherwise I get conflicting implementations).

Where it gets weird is if I get desperate and try to do satisfy the constraint by doing both:

trait TraitWithBoth<T>{
    type AssociatedType;
}
impl<T, U> AnotherTrait for T where T: TraitWithBoth<U, AssociatedType = U>{}

Then it’s back to complaining at me that U is unconstrained.

I’m struggling to understand this. How does appearing as both an associated type and a type parameter make this less constrained?

>Solution :

When defining:

impl<T, U> AnotherTrait for T {}

You must make it clear to the compiler what U is in some way and since you can’t implement AnotherTrait for T multiple times, U must be deducible to a singular type (based on T). If there are multiple possible values of U which can’t be specified another way, the compiler will not guess or pick one for you; it will throw an error. That is what it means when an error says a type is "unconstrained": you’ve forced the compiler to deduce a type but many are possible.

With that in mind, its not so complicated when you consider what traits can be implemented between a generic parameter, associated type, and both.

  • How many times can a T implement TraitWithParameter<U>? As many times as it wants because the trait is generic:

    struct MyStruct;
    impl TraitWithParameter<()> for MyStruct {}
    impl TraitWithParameter<u8> for MyStruct {}
    

    Thus U cannot be deduced and therefore would be "unconstrained" when implementing AnotherTrait for T where T: TraitWithParameter<U>.

  • How many times can a T implement TraitWithAssociatedType<AssociatedType = U>? Only once since the trait is not generic:

    struct MyStruct;
    impl TraitWithAssociatedType for MyStruct { type AssociatedType = (); }
    impl TraitWithAssociatedType for MyStruct { type AssociatedType = u8; }
    
    error[E0119]: conflicting implementations of trait `TraitWithAssociatedType` for type `MyStruct`
     --> src/lib.rs:7:1
      |
    6 | impl TraitWithAssociatedType for MyStruct { type AssociatedType = (); }
      | ----------------------------------------- first implementation here
    7 | impl TraitWithAssociatedType for MyStruct { type AssociatedType = u8; }
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyStruct`
    

    Thus U would be deduced when implementing AnotherTrait for T where T: TraitWithAssociatedType<AssociatedType = U> since there can only be one such U and can be found by T‘s implementation of TraitWithAssociatedType.

  • Now, how many times can a T implement TraitWithBoth<U, AssociatedType = U>? Well again, as many times as it wants since the trait is generic:

    struct MyStruct;
    impl TraitWithBoth<()> for MyStruct { type AssociatedType = (); }
    impl TraitWithBoth<u8> for MyStruct { type AssociatedType = u8; }
    

    Thus U once again is unconstrained and cannot be deduced based on T when implementing AnotherTrait for T where T: TraitWithBoth<U, AssociatedType = U>.

So the fact that U is assigned to an associated type doesn’t on its own mean its constrained. You must consider whether the constraint as a whole can limit deduction to a single type.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading