Antonin Carette

A journey into a wild pointer

Struct embedding trick to avoid duplicate code

Posted at — Nov 6, 2018

When you are writing go code, and try to make it more flexible, most of the time you are looking for interfaces. A go interface is a good solution to make your code more flexible, or scalable, and is also a way to achieve polymorphism.

As the official go documentation discusses about interfaces, interfaces are “named collections of method signatures”. So, to implement an interface on different structs, you have to implement each interface’s method for a given struct.

Now, let’s imagine a problem… I want to invent an RPG game, and I need two different classes of players:

Those classes can be grouped in an interface called “player”, for example:

type player interface {
  name() string
  getInformations() string
  strike(p *player) (int, error)
  stricken(p *player) (int, error)
}

type orc struct {
  name string
  age int
  health int
  size int
}

type elf struct {
  name string
  age int
  health int
  vision int
}

If I want to implement this interface for orc and elf, I have to implement name, getInformations, strike and stricken methods for both orc and elf, which is like duplicating code (except for getInformations as our struct contains different usefull informations about a given Player class).

A nice trick here is to consider that the redundant code may be included in an interface, via an embedded struct (the example has been reduced to make it more understandable):

type namer struct {
  name string
}

type get interface {
  getName()
}

type orc struct {
  namer // Instead of the name string type, we include directly the namer struct
  age int
  health int
  size int
}

type elf struct {
  namer // Same for the elf type
  age int
  health int
  vision int
}

// We implement only one method, for namer
func (n *namer) getName() {
  fmt.Println(return n.name)
}

func main() {
  o := orc{namer: namer{"Orc"}, age: 12, ...}
  e := elf{namer: namer{"Elf"}, age: 12, ...}
  o.getName() // It works, as an orc type contains the struct namer that respects
              // the deal with the get interface
  e.getName() // As well with the elf type :)
}

Using this trick, we don’t have to implement twice the same method for orc and elf, and create duplication code, which is pretty nice if you have to add 10 new structs, and a lot of new methods in the get interface ;)