Are generic types covariant? Can a Box[Dog] be used as a Box[Animal], or a Box[int] converted to a Box[MyInt]?
Question 143HardGo 1.22 to 1.25
No. Go generics are invariant. Each instantiation is a distinct named type, and there is no subtyping between instantiations, even when the type arguments are related by interface satisfaction or share an underlying type.
type Animal interface{ Sound() string }
type Dog struct{}
func (Dog) Sound() string { return "woof" }
type Box[T any] struct{ v T }
type MyInt int
var d []Dog
// var a []Animal = d // error: cannot use d ([]Dog) as []Animal
// var b Box[Animal] = Box[Dog]{} // error: different types
// var x = Box[MyInt](Box[int]{}) // error: cannot convert Box[int] to Box[MyInt]
// Fix: copy element by element
func Upcast[T Animal](xs []T) []Animal {
out := make([]Animal, len(xs))
for i, x := range xs {
out[i] = x
}
return out
}
Why the conversion fails: Box[int] has underlying type struct{ v int } and Box[MyInt] has struct{ v MyInt }. Those are different struct types, so the conversion rules do not apply. Covariance would be unsound for mutable containers anyway: if []Dog were a []Animal, you could store a Cat in it. Interviewers want "instantiations are unrelated types, convert explicitly".
More on Generics
- Q141What is
iter.Pull, and when do you need it? Implement a genericZip. - Q142What are generic type aliases (Go 1.24), and how do they differ from generic type definitions?
- Q144What is an "instantiation cycle"? Why does this recursive generic function fail to compile?
- Q145What does this print? (
%Tand reflection on generic types) - Q146Write a generic fan-in
Mergefor channels. What are the concurrency pitfalls? - Q147How do you get a pointer to a literal value generically? Compare a
Ptr[T]helper with Go 1.26new(expr).