1

I wonder how the following functions lengthAA and lengthBB can be simplified to just one function. Note in those two functions it just compute the length of the array, it is just an example and can be more complicated than that. Ideally, I want only one function that serve the same purpose (in this case, len) but can take different struct as variable.

type A struct {
   id string
}

type AA struct {
   ids []A
}

type B struct {
   id string
   value bool
}

type BB struct {
   ids []B
}

func lengthAA(aa AA) int {
   return len(aa)
}

func lengthBB(bb BB) int {
   return len(bb)
}
ryan
  • 974
  • 2
  • 7
  • 13

3 Answers3

5

The Go way of doing this would be for both AA and BB to implement a common method. length would then accept an interface that contains the same function signature. Example:

package main

import (
    "fmt"
)

type Lengther interface {
    Length() int
}

type A struct {
    id string
}

type AA struct {
    ids []A
}

func (a *AA) Length() int {
    return len(a.ids)
}

type B struct {
    id    string
    value bool
}

type BB struct {
    ids []B
}

func (b *BB) Length() int {
    return len(b.ids)
}

func length(l Lengther) int {
    return l.Length()
}

func main() {
    aa := &AA{
        ids: make([]A, 10),
    }
    bb := &BB{
        ids: make([]B, 34),
    }
    fmt.Println(length(aa))
    fmt.Println(length(bb))
}

https://play.golang.org/p/DdxP5lFcZi

1

1- Using two separate receiver methods length() like this working sample code (This is idiomatic Go):

package main

import "fmt"

func (v *AA) length() int {
    return len(v.ids)
}
func (v *BB) length() int {
    return len(v.ids)
}

func main() {
    aa := AA{[]A{A{"id"}, A{"id2"}}}
    fmt.Println(aa.length()) // 2
    bb := BB{[]B{B{"id", true}, B{"id2", true}}}
    fmt.Println(bb.length()) // 2
}

type A struct {
    id string
}

type AA struct {
    ids []A
}

type B struct {
    id    string
    value bool
}

type BB struct {
    ids []B
}

2- Using one length(aa interface{}) function, like this working sample code (in some use cases this is useful):

package main

import "fmt"

func length(aa interface{}) int {
    switch v := aa.(type) {
    case AA:
        return len(v.ids)
    case BB:
        return len(v.ids)
    }
    return -1
}

func main() {
    aa := AA{[]A{A{"id"}, A{"id2"}}}
    fmt.Println(length(aa)) // 2
    bb := BB{[]B{B{"id", true}, B{"id2", true}}}
    fmt.Println(length(bb)) // 2
}

type A struct {
    id string
}

type AA struct {
    ids []A
}

type B struct {
    id    string
    value bool
}

type BB struct {
    ids []B
}

3- Using reflect and one length(v interface{}) function, like this working sample code (in some use cases this is useful):

package main

import "fmt"
import "reflect"

func length(v interface{}) int {
    return reflect.ValueOf(v).FieldByName("ids").Len()
}

func main() {
    aa := AA{[]A{A{"id"}, A{"id2"}}}
    fmt.Println(length(aa)) // 2
    bb := BB{[]B{B{"id", true}, B{"id2", true}}}
    fmt.Println(length(bb)) // 2
}

type A struct {
    id string
}

type AA struct {
    ids []A
}

type B struct {
    id    string
    value bool
}

type BB struct {
    ids []B
}

output:

2
2
0

This code wouldn't actually compile since len(aa) would be passing a struct to len which would fail. But I think I get what you're trying to do, and the best way to do it is with the nearest thing Go has to inheritance, which is really just struct embedding.

By making an embeddable struct, and having each of your structs that have that similar feature embed it, you can reduce the line count of the code, though "simplifying" may be a bit of a stretch.

type HasIDs struct {
   Ids []string
}

type AA struct {   
   HasIDs
   OtherValues []int
}

type BB struct {
   HasIDs
   OtherValues []byte
}

At this point both structs AA and BB have the value Ids []string. Now you can give the HasIDs struct a method that both structs should be able to call.

func (hasIds HasIDs) length() {
    return len(hasIds.Ids)
}

And call the method on either struct that embeds the struct with the method.

a1 := AA{}
aLength := a1.length()

Here is a working code sample: https://play.golang.org/p/ys_CN_L_cr

Verran
  • 3,942
  • 2
  • 14
  • 22