Can we implement bivariance for 'static associated types?

Recently Discriminant<T> was changed to be invariant over T due to the use of the new DiscriminantKind trait. This change had to be made because associated types for T are always invariant with respect to T. The previous implementation was covariant:

struct Discriminant<T>(u64, PhantomData<fn() -> T);

In this case we really want bivariance: there's no reason for the type of discriminant to change regardless of lifetimes. While AFAIK this is impossible now, it seems that this should be possible in theory if the associated type has a 'static bound, eg:

trait DiscriminantKind {
    type Discriminant : 'static;
}

Being 'static, there's no way for variance from the type implementing the trait to "flow into" the associated type (modulo specialization purely on lifetimes, which IIUC isn't possible).

While this change to Discriminant<T> passed a crater run there are other API's where this is a serious limitation. For example, consider a Pointee trait for pointer metadata:

trait Pointee {
    type Metadata : 'static;
}

struct GenericBox<T: ?Sized + Pointee, P> {
    thin: P,
    metadata: T::Metadata,
}

I have a project with an API along those lines, and had to add a third M parameter that defaults to <T as Pointee>::Metadata due to variance issues, an ugly workaround.

What would it take to implement at least opt-in bivariance in this situation?

See https://github.com/rust-lang/rust/pull/71896 for a discussion about the variance of associated types

1 Like

Interesting! Maybe if the compiler can do it as a bug for trait objects it can do it as a feature elsewhere. :joy:

1 Like