Stricter Go enums with go-genums

gdm85
gdm85
Sep 18, 2015 · 3 min read

Go has an idiomatic way to declare enums, that is by many (me included) praised for its simplicity:

type Day intconst (
Monday Day = 1 + iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)

Other languages have usually more features for enum types, for example in Java:

public enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}

And then you can use features like:

for (Day day: Day.values()) {
if (day == Day.Monday) {
System.out.printf("Monday is day number %d\n", day.ordinal());
}
System.out.printf("Day of the week: %s\n", day);
}

In C# you can benefit from similar type-safety checks, and a slightly different syntax for the base type:

enum Days : byte {Monday=1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};

Let’s give now another look at the same enum in Go, with focus on what we could make use of:

type Day int // a String() method for each value, so that we can printf it during development/logging?const (
Monday Day = 1 + iota // more complex types maybe?
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
// boundary check so that I cannot inadvertently create an 8th day of the week?
)
// array/slice of allowed values?

In a discussion with other Go coders I came up with the idea of using a slightly stricter syntax for Go enums, (ab)using type switches and the marvels of templating (with go:generate), the result of which is the go-genums project.

The generated code for this weekdays example with go-genums is (some parts redacted for simplicity):

package main

// *** generated with go-genums ***

// DayEnum is the the enum interface that can be used
type DayEnum interface {
String() string
Value() dayEnum
uniqueDayMethod()
}

// dayEnumBase is the internal, non-exported type
type dayEnumBase struct{ value dayEnum }

// Value() returns the enum value
func (eb dayEnumBase) Value() dayEnum { return eb.value }

// String() returns the enum name as you use it in Go code,
// needs to be overriden by inheriting types
func (eb dayEnumBase) String() string { return "" }

// ... [redacted declaration of a type+methods for each allowed enum value]

var internalDayEnumValues = []DayEnum{
Sunday{}.New(),
Monday{}.New(),
Tuesday{}.New(),
Wednesday{}.New(),
Thursday{}.New(),
Friday{}.New(),
Saturday{}.New(),
}

// DayEnumValues will return a slice of all allowed enum value types
func DayEnumValues() []DayEnum { return internalDayEnumValues[:] }

// NewDayFromValue will generate a valid enum from a value, or return nil in case of invalid value
func NewDayFromValue(v dayEnum) (result DayEnum) {
switch v {
case daySunday:
result = Sunday{}.New()
case dayMonday:
result = Monday{}.New()
case dayTuesday:
result = Tuesday{}.New()
case dayWednesday:
result = Wednesday{}.New()
case dayThursday:
result = Thursday{}.New()
case dayFriday:
result = Friday{}.New()
case daySaturday:
result = Saturday{}.New()
}
return
}

// ... [redacted]

The resulting generated enum code is nonetheless longer and more complex, but you do not have to type it as it’s auto-generated; among the features added by this code generation step, the one I personally like most is the possibility to use type switches for enum evaluation:

day := Monday{}.New()switch day.(type) {
case Monday:
fmt.Println("It's", day)
default:
panic("It's not Monday!")
}

Note here that a type switch over an interface value is still pretty efficient, as it will not de-reference the actual value (be it an int or a large struct) but rather use Go’s internal type id.

Last but not least, with go-genums you can use structs for your enum declaration and auto-generated validating factory methods, and you do not loose the capability of directly comparing enum variables; you can read more and play around with it at the github project page.

Where do we Go now

8-bit adventures in Linux-land, with Go & other geeky…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store