December 17, 2020

How to Use Embedding to Write Cleaner Code in Go?

Know how borrowing a type can give you clean code

Abstract

Go is cleaner because it doesn’t support many of the features that tradition OO languages support. Less is more. Inheritance is one of those features but Go has a different style which can fulfil the necessity of inheritance. It’s called Embedding.

Table of content

  • What is embedding
  • How to create embedding
  • Embedding In Interface
  • Embedding In Struct
  • Conclusion
  • Reference

What is embedding

You can “borrow” the functionality of a struct or interface into another struct and interface respectively is called embedding. You can not embed an interface into a struct or vice versa.

How to create embedding

To create an embedding you need to put a different struct or interface into a different one without naming the field.

type Morning struct {
  breakfast bool
}

type Routine struct {
  Morning // ⬅️ no field name makes it embedded }

type Director interface {
  Direct()
}

type Producer interface {
  Produce()
}

type Creator interface {
  Director  Producer}

Embedding In Interface

Interface when embedded is the union of all the methods present in all of the interfaces combined. If method Eat() is present on an interface and Sleep() on another then embedding them into an interface with Repeat() method will have all, Eat(), Sleep(), and Repeat() methods.

Embedding In Struct

Embedding in a struct is where you can see the real power of Go. Not only the fields that defined type has but also the methods you can access them directly. How cool is that! Let’s continue with our Routine struct.

type Morning struct {
  breakfast bool
}

func (m Morning) hasBreakfast() bool {  return m.breakfast}
type Routine struct {
  Morning
}

r := Routine{}

r.hasBreakfast() // false

You also the embedded type will be a field name on the struct. For example, you can have:

type DailySports struct {
  Jog            // field name Jog
  *Swim          // field name Swim
  sports.Cycling // field name Cycling
  *sports.Tennis // field name Tennis
}

func (d DailySports) enrolledBySwimming() bool {
  return d.Swim.Speed}

But you cannot have conflict while embedding. This makes sense otherwise you won’t know which one to pick.

struct {
  *Swim         // ⛔ Now allowed
  *sports.Swim  // ⛔ Not allowed
}

You can do all the things you can with the promoted fields but you can not use the field name present in the defined type while constructing the struct.

type Morning struct {
  breakfast bool
}

type Routine struct {
  Morning
}

r := Routine{breakfast: true} // ⛔ Not allowed

Conclusion

You can use embedding to use any defined type to used inside a struct and borrow all the functionality it has. You need to check that the names don’t clash otherwise you are good to go.

Reference

  1. https://golang.org/doc/effective_go.html#embedding
  2. https://golang.org/ref/spec#Struct_types