Consider this code:
type Rectangle struct {
Width, Height, Area int
}
type Square struct {
Side, Area int
}
type Geometry struct {
Area int
}
func SumGeometries(geometries ...Geometry) (sum int) {
for _, g := range geometries {
sum = g.Area
}
return
}
func TestSumGeometries(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(rect, square) // cannot use rect (variable of type Rectangle) as Geometry value in argument to MyFunc compilerIncompatibleAssign
want := 45
if got != want {
t.Error("fail!")
}
}
I want MyFunc to take whatever struct that contains Apple, not just BStruct in specific.
Is this achievable in Go?
The only way I can find ATM is the following:
type Rectangle struct {
Width, Height, Area int
}
func (r *Rectangle) GetArea() int {
return r.Area
}
type Square struct {
Side, Area int
}
func (s *Square) GetArea() int {
return s.Area
}
type Areaer interface {
GetArea() int
}
func SumGeometries(geometries ...Areaer) (sum int) {
for _, s := range geometries {
sum = s.GetArea()
}
return
}
func TestArgs(t *testing.T) {
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
got := SumGeometries(&rect, &square) // cannot use rect (variable of type Rectangle) as Geometry value in argument to MyFunc compilerIncompatibleAssign
want := 45
if got != want {
t.Error("fail!")
}
}
It feels perhaps not idiomatic: would I want to pollute my struct with an unnecessary method when I'd be already happy with consumers accessing the data directly?
CodePudding user response:
Adding methods to types it not "polluting".
But there's a way to achieve what you want without repetition. Define an Area
type holding the common (here Area
) field with GetArea()
method:
type Area struct {
Value int
}
func (a Area) GetArea() int {
return a.Value
}
And embed this in other types:
type Rectangle struct {
Width, Height int
Area
}
type Square struct {
Side int
Area
}
This way the GetArea()
method gets promoted, and Rectangle
and Square
will automatically implement Areaer
. Testing it:
rect := Rectangle{5, 4, Area{20}}
square := Square{5, Area{25}}
got := SumGeometries(rect, square)
want := 45
if got != want {
fmt.Println("fail!")
}
Outputs nothing (no error). Try it on the Go Playground.
Note that if Area
contains a single field only, you can even omit the wrapper struct and use int
as the underlying type directly:
type Area int
func (a Area) GetArea() int {
return int(a)
}
Then using it is simpler:
rect := Rectangle{5, 4, 20}
square := Square{5, 25}
Try this one on the Go Playground.