Home > Blockchain >  Golang: implicit struct matching
Golang: implicit struct matching

Time:09-04

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.

  • Related