Not quite. A covariant type constructor K<_>: type -> type is such that if T <: U, then K<T> <: K<U>. By Rust’s coercion rules, if T <: U, then an expression that types as K<T> may be coerced to K<U>. Rust doesn’t have inheritance, so the main places people care about are
&mut T <: &T
T <: dyn Trait
[T; _] <: [T]
! <: T
For example, because &T is covariant, &! may be coerced to any &T you want, though &! is impossible to obtain without invoking UB along the way:
(&0 as *const _ as *const !).as_ref().unwrap()
Now, it is kosher for [!; 0] to be contravariant, by the same argument for covariance. I don’t know that this is useful though. Now you get to convert any [T; 0] to any [U; 0]… which introduces unnecessary special-casing in the compiler, and which could be achieved by declaring in the nomicon that
transmute::<[T;0], [U;0]>(..)
is never UB. Since the point of making [T; 0] covariant in T is to allow for [] to have a default type without introducing an unnameable [?; 0] type, saying “just allow this transmute and don’t mess with current variance” defeats the point.