← Today I Learned

Represent a sum type

Let’s say we have two structs in Go:

type Foo struct {
  One string
}

type Bar struct {
  Two int
}

How do we create a sum type — a type that can hold either one of our structs? Go doesn’t have subtypes (or does it?) but it does have a feature called interfaces.

An interface describe a set of methods that a type must implement. When a type implements all the methods, we say it satisfies the interface.

The trick here is to create an interface with a single stub method (I’ve called it _type here, but that name is arbitrary). We then implement that stub method with an empty function for each struct type:

type Union interface {
  _type()
}

func (u Foo) _type() {}
func (u Bar) _type() {}

When we have a variable that satisfies the interface, we can switch on its underlying type:

func PrintUnion(unknown Union) {
	switch thing := unknown.(type) {
	case Foo:
		// thing has type Bar in this branch
	  fmt.Printf("%s\n", thing.One)
	case Bar:
	  // thing has type Bar in this branch
	  fmt.Printf("%d\n", thing.Two)
	}
}

This technique was mostly cribbed from this Zack Radisic blog post about sum types in Go.