Hey everyone. I have some code that’s compiling and running despite there being a type error. I don’t understand go generics and interfaces that well, so I don’t know if this behavior is intended. I have a few different versions of this issue, but here’s a simple one:
type Wrap[A any] interface {
isWrap()
}
type Val[A any] struct {
val A
}
func (v Val[A]) isWrap() {}
func Extract[A any](w Wrap[A]) {
switch w.(type) {
case Val[A]:
// do something
default:
panic("shouldn't happen")
}
}
func main() {
Extract[string](Val[int]{3})
}
The Extract function takes a Wrap[A] and does a type switch on its implementations. The main function calls Extract with an input Val[int], but it explicitly instantiates the function’s generic to be of type string. So a Val[string] input would be correct, but Val[int] should be a type error. Despite this, the code compiles and panics at runtime, presumably because the case Val[A] expects A to be a string, not an int.
I got some similar examples to compile and run, they all involved instantiating a generic interface with one type but using it as if it had a different type. Is this behavior intended, or maybe is there some implicit unsafe cast happening? Or is this a bug?
For the records, the golang-nuts email list has some answers.
TL;DR:
Answer from Ian Lance Taylor:
The Wrap interface ignores its type parameter, so any type that
implements a isWrap method with no arguments and no results will
implement Wrap with any type argument. The argument to Extract is
Wrap[A], so you can pass anything with an isWrap method to Extract.
The type Val[int] does have an isWrap method, so it’s fine to pass
that type to Extract[int].
Then it’s clear that the parameter “w Wrap” to func Extract is just a plain old interface type, unrelated to generics. Dynamically at runtime it can be nil, or a value of any type which implements the Wrap interface.
Interfaces aren’t implemented by structural inheritance, they’re just duck-typing: “does the concrete type implement this method set?” Therefore, these interfaces are identical and interchangeable: