2

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?

Arun
  • 19,750
  • 10
  • 51
  • 60
  • 1
    if your Foo and Bar come from external packages, see also this, which is specific to that issue https://stackoverflow.com/questions/72664674/generic-function-to-work-on-different-structs-with-common-members-from-external – blackgreen Nov 10 '22 at 13:52

1 Answers1

2

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

zangw
  • 43,869
  • 19
  • 177
  • 214
  • 1
    Thanks! This is a reasonable approach. In my real situation (from where I created the example), the types `Foo` and `Bar` comes from external packages. I won't be able to add methods to them :( Thank you for the reference to the go documentation, I was not aware that this case is captured in the release note. – Arun Nov 09 '22 at 02:16