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.

Reply via email to