Issue
I would like to make the following code compile. My understanding from reading the Type Parameters Proposal (Go Generics) is that this should work, but I must be missing something.
package main
import "fmt"
func main() {
s := Struct{A: "Hello World!"}
PrintA(s)
}
func PrintA[T Type](v T) {
fmt.Printf("%s\n", v.A)
}
type Type interface {
struct{ A string }
}
type Struct struct {
A string
}
func (s Struct) String() string {
return s.A
}
The error I get is:
./prog.go:7:8: Struct does not implement Type (possibly missing ~ for struct{A string} in constraint Type)
./prog.go:11:23: v.A undefined (interface Type has no method A)
I would like T
to represent all structs with a particular field of a particular type. Adding ~
did not help.
Here’s an example from the proposal that was implemented and is part of the latest Go beta release.
type structField interface {
struct { a int; x int } |
struct { b int; x float64 } |
struct { c int; x uint64 }
}
https://go.dev/play/p/KZh2swZuD2m?v=gotip
Solution
Field access has been completely disabled for Go 1.18. The Go 1.18 release notes mention this:
The current generics implementation has the following known limitations:
[…]
- The Go compiler does not support accessing a struct field
x.f
wherex
is of type parameter type even if all types in the type parameter’s type set have a fieldf
. We may remove this restriction in Go 1.19.
The workaround for any struct type boils down to the old boring interface-based polymorphism that we all have been using so far without type params:
type Type interface {
GetA() string
}
func (s Struct) GetA() string {
return s.A
}
And at this point you don’t even have to use the Type
interface as a constraint. It can just be a plain interface type:
func PrintA(v Type) {
fmt.Printf("%s\n", v.GetA())
}
Old answer
At some point in early 2022 while this feature was still in development, your example did work if you added ~
:
type Type interface {
~struct{ A string }
}
but it only worked for structs exactly defined as struct{ A string }
and nothing else. Defining a constraint that "represent[s] all structs with a particular field of a particular type" was never supported all along. See this answer for details.
Instead the example you quote from the proposal is about accessing a common field in a type set. By defining a union of structs:
type structField interface {
~struct { a int; x int } | ~struct { a int; x float64 }
}
you should be able to access the field a
of such a type parameter, but again this wasn’t implemented, as mentioned at the beginning of the answer. It used to work if all terms in the union had the same underlying type (example adapted from issue #48522):
package main
import "fmt"
type Point struct {
X, Y int
}
type Rect struct {
X, Y int
}
func GetX[P Point | Rect] (p P) int {
return p.X
}
func main() {
p := Point{1, 2}
r := Rect{2, 3}
fmt.Println("X: %d %d", GetX(p), GetX(r)) // prints X: 1 2
}
This code doesn’t compile anymore as of March 2022.
Answered By – blackgreen
Answer Checked By – Pedro (GoLangFix Volunteer)