I am working with a family of types that integrate with an existing Go project that follow a couple of patterns, and I'd like to manipulate them uniformly by introducing a generic function. My example uses a contrived, whittled-down set of struct fields and interfaces to demonstrate the challenge. Here <https://go.dev/play/p/bI4xqlrPB5c>'s all the code together in the Go Playground.
Suppose there's an interface called Object. I'm not responsible for the name or the idea; it comes from a public project. type Object interface { GetKind() string GetName() string } I have a couple of types (named X and Y here) that both implement this interface via pointer receiver. The project has many integration points that expect to manipulate pointers to structs, so even though I *could* implement these methods by value, adhering to this project's conventions has me writing these methods with pointer receivers. Each has a companion type named with the suffix List that contains a slice of values of the basic type. type X struct { Name string } func (*X) GetKind() string { return "X" } func (x *X) GetName() string { return x.Name } type XList struct { Items []X } func showXs(xs XList) { fmt.Println("Items:") for i := range xs.Items { o := &xs.Items[i] fmt.Printf("- Item %d of kind %s (type %T): %q\n", i, o.GetKind(), xs.Items[i], o.GetName()) } } type Y struct { Name string } func (*Y) GetKind() string { return "Y" } func (y *Y) GetName() string { return y.Name } type YList struct { Items []Y } func showYs(ys YList) { fmt.Println("Items:") for i := range ys.Items { o := &ys.Items[i] fmt.Printf("- Item %d of kind %s (type %T): %q\n", i, o.GetKind(), ys.Items[i], o.GetName()) } } Observe that the showXs and showYs functions are nearly identical. I'd like to write one function that could consume an XList or a YList—or any other similar type. At least as of Go 1.18, I can't constrain a type parameter structurally by requiring that it have an "Items" field, so I introduce an interface with a method to retrieve the items. type HasItems[E any] interface { GetItems() []E } Now, I adapt XList to play along with this interface. type AugmentedXList struct { *XList } func (l AugmentedXList) GetItems() []X { return l.Items } Now, when I attempt to write a generic show function that consumes a HasItems and uses each item as an Object, I run into this challenge: How do I express that some item type E implements the Object interface, not directly by by pointer receiver? I wrote this show function using a projection function callback, converting from *E to Object: func show[E any](is HasItems[E], project func(*E) Object) { fmt.Println("Items:") items := is.GetItems() for i := range items { o := project(&items[i]) fmt.Printf("- Item %d of kind %s (type %T): %q\n", i, o.GetKind(), items[i], o.GetName()) } } Finally, here's a sample invocation of the showXs, showYs, and show functions: func main() { xs := XList{ Items: []X{ X{"one"}, X{"two"}, }, } showXs(xs) ys := YList{ Items: []Y{ Y{"three"}, Y{"four"}, }, } showYs(ys) show[X](AugmentedXList{&xs}, func(x *X) Object { return x }) } Note that the function I'm passing to the show function is the identity projection. That is, a *X is an Object. Should it be possible to write a type constraint that would allow me to eliminate this projection function parameter, and instead have the compiler mandate and ensure that for a given type E, the related type *E implements Object? -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/ea28d3ef-1835-4c66-8530-46d385677341n%40googlegroups.com.