go generics: processing different struct types with same data member types

Issue

There are two struct types, Foo and Bar, with an int data member val. I am trying to write a generic function that can handle both types. I tried the following and this did not work.

package main

import "fmt"

type Foo struct {
    val int
}

type Bar struct {
    val int
}

func Add[T any](slice []T) int {
    var sum int
    for _, elem := range slice {
        sum += elem.val
    }
    return sum
}

func Test() {
    f1 := Foo{val: 2}
    f2 := Foo{val: 2}
    fslice := []Foo{f1, f2}
    fsum := Add(fslice)
    fmt.Printf("fsum = %d\n", fsum)

    b1 := Bar{val: 3}
    b2 := Bar{val: 3}
    bslice := []Bar{b1, b2}
    bsum := Add(bslice)
    fmt.Printf("bsum = %d\n", bsum)
}

func main() {
    Test()
}

The compiler throws the following error.

$ go run generics1.go
# command-line-arguments
./generics1.go:16:15: elem.val undefined (type T has no field or method val)

Go playground link: https://go.dev/play/p/mdOMH3xuwu7

What could be a possible way to approach this?

Solution

Per golang 1.18 release note

The Go compiler does not support accessing a struct field x.f where x is of type parameter type even if all types in the type parameter’s type set have a field f. We may remove this restriction in a future release.

You could define one GetVal() interface method to retrieve the val, and use this method as part of type constraint of generic.

Sample codes

type Foo struct {
    val int
}

func (f Foo) GetVal() int {
    return f.val
}

type Bar struct {
    val int
}

func (b Bar) GetVal() int {
    return b.val
}

type MyType interface {
    Foo | Bar
    GetVal() int
}

func Add[T MyType](slice []T) int {
    var sum int
    for _, elem := range slice {
        sum += elem.GetVal()
    }
    return sum
}

https://go.dev/play/p/0eJZpqy7q8f

Answered By – zangw

Answer Checked By – Dawn Plyler (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.