This post will describe how to implement and use enumerations (or enum types) in Go.
Enums are types that contain only a limited number of fixed values, as opposed to types like int
or string
which can have a wide range of values.
This is useful for many situations: For example, when describing seasons in temperate climates, you’ll want to limit the options to certain possible values: Summer
, Winter
, Spring
and Autumn
.
Let’s look at different ways of implementing this type of data:
Defining Enums as Constants
The simplest way to implement enums for season options is to create an int
or string
constant for each season:
// int mapping
const (
Summer int = 0
Autumn = 1
Winter = 2
Spring = 3
)
// string mapping
const (
Summer string = "summer"
Autumn = "autumn"
Winter = "winter"
Spring = "spring"
)
While this would work for small codebases, we will face a few immediate issues:
It’s easy to make mistakes in your code. A developer can make the mistake of using integers outside the range of the ones defined.
Definitions from unrelated enums can overlap and cause conflicts:
const ( Summer int = 0 Autumn = 1 Winter = 2 Spring = 3 ) const ( Apples = 0 Oranges = 1 ) func main() { // Ideally, this should never be true! fmt.Println(Summer == Apples) }
This is semantically incorrect - Seasons are not really integers or strings, they’re seasons!
Enum Types
We can define a new Season
type with int
as the base type:
type Season int64
const (
Summer Season = 0
Autumn Season = 1
Winter Season = 2
Spring Season = 3
)
This defines the above constants as their own types. You can also use the iota identifier to achieve the same result in a more readable manner:
const (
Summer Season = iota
Autumn
Winter
Spring
)
Using iota instead of manually assigning values also prevents you from inadvertently assigning the same value to more than one constant, which could otherwise cause a conflict. It also carries over the type, so we don’t have to declare
Season
every time.
Since Season
is a new type, you can’t compare it to other types anymore:
func printSeason(s Season) {
fmt.Println("season: ", s)
}
func main() {
i := 0
printSeason(i)
// This will result in a compilation error since
// i is an int, and `printSeason` only accepts
// Season types
}
It’s now impossible to compare Season
constants with Fruit
constants, or use them interchangeably.
Adding String Methods to Enum Types
In the previous example, if we call printSeason(Winter)
, we will get the output:
season: 2
Try this on the Go playground
It would be better if we can a more human-readable format when printing our season selection.
Let’s implement the String
method for the Season
type to achieve this:
const (
Summer Season = iota
Autumn
Winter
Spring
)
func (s Season) String() string {
switch s {
case Summer:
return "summer"
case Autumn:
return "autumn"
case Winter:
return "winter"
case Spring:
return "spring"
}
return "unknown"
}
If we run the code now, we should get the output:
season: winter
We can add any number of methods to this type as we require.
Default Enum Values
In our previous examples, we defined our Season
constants as integer values. This means that 0
(which is Summer) would be the default value.
When using enum types, you should make sure that you’re ok with the default value. For example, if you want the value to be set explicitly, it’s better to add a custom “unknown” or “undefined” value:
const (
// since iota starts with 0, the first value
// defined here will be the default
Undefined Season = iota
Summer
Autumn
Winter
Spring
)
This can be useful when you want to check if a value has been set or not:
if mySeason == Undefined {
// log an error
}
If we were using string enums (type Season string
) instead of ints, how would you rewrite the constant definition for the Undefined
value?
Since the default value of a string in Go is the empty string (""
), this is what your default enum should be assigned to as well.
Using Enums in Practice
Now that you know how to implement enums, the next question is: When to use them?
We can see enums used throughout the standard library. Some common examples are:
- Prefix flags in the log package
- Status codes and methods in the http package
- Standard grayscale colors in the color package
In general, you should use enums whenever you have group of limited, and related values for a particular type.
Have you used enums, or something similar to enums in your code before? How was is useful? Let me know in the comments!