Why is this type invariant?

I've been debugging some gnarly lifetime issues and have narrowed down the problem to a type being invariant when it should be covariant. I've reproduced it with a minimal example program:

// type TestType<'a> = Covariant<&'a u8>; // works if uncommented
type TestType<'a> = Invariant<&'a u8>; // doesn't work

// This struct is covariant.
struct Covariant<T>(T);

// This struct is invariant, but I don't know why.
struct Invariant<T: Item>(T::Type);
trait Item { type Type; }
impl<T> Item for T { type Type = T; }

// Verify that `TestType` is covariant.
fn test_covariance<'short, 'long: 'short>() {
    let long: TestType<'long> = make();
    let short: TestType<'short> = long;
}

fn make<T>() -> T { unimplemented!() }

Error:

error: lifetime may not live long enough
  --> src/main.rs:16:15
   |
15 | fn test_covariance<'short, 'long: 'short>() {
   |                    ------  ----- lifetime `'long` defined here
   |                    |
   |                    lifetime `'short` defined here
16 |     let long: TestType<'long> = make();
   |               ^^^^^^^^^^^^^^^ type annotation requires that `'short` must outlive `'long`
   |
   = help: consider adding the following bound: `'short: 'long`
   = note: requirement occurs because of the type `Invariant<&u8>`, which makes the generic argument `&u8` invariant
   = note: the struct `Invariant<T>` is invariant over the parameter `T`
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

(Explorer)

For reasons I don't understand, Invariant is invariant over T, even though it is intended to be covariant. Why does passing T through the Item trait make it invariant?

Associated types are invariant. Since there's no way to constrain the variance of associated types, the compiler has to be conservative and assume only invariance is safe.

6 Likes

The compiler could in theory be smarter than it currently is here. In the OP example, the blanket impl of Item constrains Type's variance for all T: Sized, but that information isn't used in variance calculation. (I asked about this on Zulip a few days ago)

1 Like