type A int
func (A) Hi() {
}
func DoA[T A](t T) {
t.Hi() // this line compiles err~
}
t
can only be a instance of A
, but t.Hi()
gives a compilation error:
t.Hi undefined (type T has no field or method Hi)
Instead, If I define an interface:
type I interface{
Hi()
}
and use I
as the type constraint, it compiles fine:
func DoA[T I](t T) {
t.Hi() // this line compiles ok
}
I do not quite understand it. Can you tell me why it works this way?
It's a compiler restriction (or an unfixed bug) that was mentioned in Go 1.18 release notes:
The Go compiler only supports calling a method m on a value x of type parameter type P if m is explicitly declared by P’s constraint interface.
When you use A
as a type constraint, that's syntactic sugar for interface{ A }
, which does not explicitly declare the method Hi()
, whereas I
does.
You can make it work with:
type C interface {
A
Hi()
}
func DoA[T C](t T) {
t.Hi() // works
}
Unfortunately, the language specification doesn't clearly mention this restriction, because the intention was to allow the behavior. In fact, the specification reads as if this is allowed:
The method set of an interface type is the intersection of the method sets of each type in the interface's type set (the resulting method set is usually just the set of declared methods in the interface).
Given that a type constraint is an interface type (if we ignore the syntactic sugar), the method set of interface{ A }
does comprise Hi()
.
This is why this issue might be considered an unfixed compiler bug.
You can find it in the Go issue tracker: spec: Method sets section doesn't seem quite right for interfaces with type lists. Given the recent proposal to remove the notion of "core type" from the language, this might be a good candidate for release in Go 1.24.