loading...

Representing enums in go

shiraazm profile image Shiraaz Moollatjie ・3 min read

This article will look at how to represent enums in go. It will also add some practical advice as well.

An enum is a datatype that consists of a set of named values. Many languages use the enum keyword to represent enum types.

Use iota to define enums

According to the Go specification, iota is a keyword that represents successive untyped integer constants as long as those constants are in the same constant declaration.

For example, we can define the following set of constants from c0 -> c2:

const (
    c0 = iota  // c0 == 0
    c1 = iota  // c1 == 1
    c2 = iota  // c2 == 2
)

We can reduce the amount of code written by removing the excess iota declarations:

const (
    c0 = iota
    c1
    c2
)

The iota keyword also lets us define specific patterns with your constants, but in this article we are only focusing on enums.

Applying this in practice

So let us apply this in practice. We will use the example of card suites. So we define a suite of cards:

type Suite int

const (
  unknown Suite = iota
  Spades
  Hearts
  Diamond
  Clubs
  sentinel
)

Because our eyes are so sharp, you'd notice that I added two new values to this list: An unknown and a sentinel value. Let us work through their value.

Using unknown and sentinel values

Within go, generally we want our enum type to be able to support zero values. This makes it easier to write idiomatic go without worrying about processing business logic for zero values.

There are cases where zero values needs to be supported (e.g. for some externally defined protocol or even legacy code), but within our own application code we try to prevent it.

The purpose of the sentinel is to make it easy to iterate and validate our list of enum values. It also adds to the maintainability of our enums.

For example, because Suite is it's own type, we can in theory assign it any value. So let's try to validate that an integer is within our list of enums:

func (s Suite) isValid() bool {
  return  s > unknown && s < sentinel
}

As you can see, our validation code is agnostic of the specific values of our suites. This means that we are able to add values to our enums as long as they appear before our sentinel value.

Deprecating enums?

If your system is small, then this wouldn't really apply. However, suppose you do not want to support certain types for whatever reason. Go actually makes this very easy by using _ values. So let's say that we only want to use the black Suites, but we need to keep the red Suites for historical purposes. We simply do this:

type Suite int

const (
  unknown Suite = iota
  Spades
  _
  _
  Clubs
  sentinel
)

Using this way, we are maintaining the Clubs data throughout the lifetime of our application, and we will be able to catch all the usages of the red Suite values during refactoring.

Conclusion

In this post we looked at how to define enums in go using the iota keyword with our constants. We also recommended the use of an unknown value and a sentinel value to make application programming easier. We even took a look at possibly deprecating enum values in our application.

Posted on by:

Discussion

pic
Editor guide