Follow-up question about type constraints

Hey folks,

I'm refering to this thread from a few days ago.

I played around a bit further and also tried with GATs:

trait MyTrait {
    type Gat<U>;
}

struct MyStruct<F, T, U>
where
    F: MyTrait<Gat<U> = T>,
{
    f: F,
}

Although it's now a GAT instead of an AT there can still only exist one impl of the trait per type. So shouldn't this work?

Regards
keks

1 Like

The problem is that knowing F does not mean that you also know what U is.

1 Like

Ahh I think I know where my mistake lies. First I thought it should work because I assumed it's completely irrelevant in this case what U actually is when I say Gat<U> = T but it could also be for example that there are bounds at the GAT in the trait where U is involved and when that's the case it would of course be necessary to actually know U!

It's more like...

impl MyTrait for i32 {
    type Gat<X> = ();
}

struct MyStruct<F, T, U>
where
    F: MyTrait<Gat<U> = T>,
{
    f: F,
}

If I tell you F = i32 and T = (), can you tell me what U is?

1 Like

Yes, it's not clear what U actually is but my assumption - according to my initial example - was that the question what U is doesn't even arise here but there can of course be cases when the concrete type you assign in an impl to Gat depends on U in one way or another and then you have to know U of course.

Side question: Might this work in the future when we maybe get fully-fledged HKTs?

It arises because the compiler wants to know :slight_smile:.

More precisely, for every generic parameter T of a struct, the compiler wants to determine if T is invariant, covariant, or contravariant in Struct<.., T, ..>.[1] If it can't do that, then T must be constrained -- able to be determined by the other (constrained) parameters.

Why? Because the compiler wants to infer variance, and bivariance -- where the parameter can arbitrarily coerce between types[2] -- is considered "not good". For instance, here's an example that errors out because the variance of T could not be constrained, even though it did show up in a field.[3]

Note that the RFC calls out the "constrained associated type" case as an exception. Those are, in fact, bivariant! (Though I've never seen it come up in "real" code.) I think it would not be observable if you couldn't implement traits differently for super-and-sub types, but the current plan is to keep accepting that, despite the future compat warning. The fact that it is observable may have been an oversight.[4]

Invariance or variance annotations are possible alternatives to rejection.

I doubt we will, but if I'm wrong... I don't actually know what the implications of HKTs are here, I'd have to do some research and thinking :slightly_smiling_face:.


  1. This goes for lifetime parameters too. ↩︎

  2. There are two definitions of bivariance in common use. One is "covariant and contravariant". The other is "can coerce to anything". Rust uses the latter. ↩︎

  3. The error at least is much better than it used to be. ↩︎

  4. AFAIK you can't do this with lifetime parameters, just type parameters. ↩︎

1 Like

As always, thank you very much for your great help! :slight_smile:

I hadn't variance in mind at all when I was thinking about this topic but you're right of course!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.