I am using Generics with Go 1.21. I want to extend some common code.
If I add foo() below to the Under11AgeGroups interface so only the Under 9s and Under 11s have to implement this function, I get a compile error:
Cannot use interfaces with methods in union.
Am I missing a way to do this ?
The below code runs.
https://go.dev/play/p/w_N7gbsRCjY
package main
import (
"fmt"
)
type AllAgeGroups interface {
Under13Player | Under11AgeGroups
name() string
}
type Under11AgeGroups interface {
Under9Player | Under11Player
// foo() string <-- this causes cannot use main.Under11AgeGroups in union (main.Under11AgeGroups contains methods)
}
type Players[T AllAgeGroups] struct {
Results []T
}
func (t *Players[T]) PrintTheTeam() {
fmt.Println("----------------")
for _, p := range t.Results {
fmt.Println("Player ", p.name())
}
}
type Under9Player struct { Name string }
func (u Under9Player) name() string {
return u.Name
}
type Under11Player struct { Name string }
func (u Under11Player) name() string {
return u.Name
}
type Under13Player struct { Name string; Age int }
func (u Under13Player) name() string {
return u.Name + " " + string(u.Age)
}
func main() {
bob := Under11Player{Name: "bob"}
alice := Under11Player{Name: "alice"}
yves :=Under11Player{Name: "yves"}
under11team := Players[Under11Player]{Results: []Under11Player{bob, alice, yves}}
under11team.PrintTheTeam()
foo :=Under13Player{Name: "foo", Age: 12}
under13team := Players[Under13Player]{Results: []Under13Player{foo}}
under13team.PrintTheTeam()
}
You can't directly add a method to an interface that already appears in a union term. This is forbidden by the language specifications (paragraph about Interface types):
However there's a trick! Since you are using these interfaces as constraints, they provide a compile-time check. You can replicate that compile-time check by attempting to instantiate a type parameter constrained to the method with the desired types:
Note that
mustDefineFoo[Under9Player]are function values. It doesn't actually matter what these are, they exist only to force the compiler to check ifUnder9PlayerandUnder11Playersatisfy the constraint: they do if they have thefoo() stringmethod.Playground: https://go.dev/play/p/2QXzMsw1Qld
Clearly, every time you add a new term to the main interface constraint
Under11AgeGroups, you have to add a new dummy instantiation to your source code, but this isn't much different than adding one more term to the existing union.