In Go, there is no enum data type known from languages such as C++, Java, or Python. However, this does not mean that enums cannot be created. If you declare constants of custom type and some helper methods, you get a structure very similar to enum in other programming languages.
See the example below to check how to declare enum in Go.
In the example, we use constants declaration with
iota. Check our tutorial on how to useiotain Go.
package main
import (
"fmt"
)
type Season int
const (
Spring Season = iota + 1
Summer
Autumn
Winter
)
func (s Season) String() string {
seasons := [...]string{"spring", "summer", "autumn", "winter"}
if s < Spring || s > Winter {
return fmt.Sprintf("Season(%d)", int(s))
}
return seasons[s-1]
}
func (s Season) IsValid() bool {
switch s {
case Spring, Summer, Autumn, Winter:
return true
}
return false
}
func main() {
mySeasons := []Season{Spring, Summer, Autumn, Winter, 6}
for _, s := range mySeasons {
fmt.Printf("season: %s, is valid: %t\n", s, s.IsValid())
}
}
Output:
season: spring, is valid: true
season: summer, is valid: true
season: autumn, is valid: true
season: winter, is valid: true
season: Season(6), is valid: false
How it works
- Declare custom type and elements of our
enumtype Season int const ( Spring Season = iota + 1 Summer Autumn Winter )As a first step, we declare new integer type
Seasonand create successive constant values using theiotakeyword.Why do we use int type instead of string?
Constants are usually used to compare to a variable, e.g.
if Spring != season { // do something }and often we do not need their specific values, just the fact that the constant and the variable are different. In such a case comparing two
intvalues is faster than comparing two strings. Constants of typeintalso take less memory space.Why do we declare the custom type
Seasoninstead of using untyped constants?A custom type protects against passing invalid values already at the compilation stage. If we declared seasons as untyped constants, the code below of assigning invalid season value 6 to the variable would execute without any error:
var d int = 6 season := Spring season = dWhen using
Seasontype, to make this code run without any error, you need to explicitly convert thedvariable, so it is not possible to do it by accident:var d int = 6 season := Spring season = Season(d)Also, a custom type of
enummakes the code cleaner. For example, declaring the functionfunc foo(s Season)you immediately see that the function requires season as an argument, and in the case of
func foo(s int)you only know that it needs an integer argument, without any semantic meaning. Thanks to the custom types we also can add methods to the
enumelements likeString()andIsValid(). - Create
String()methodfunc (s Season) String() string { seasons := [...]string{"spring", "summer", "autumn", "winter"} if s < Spring || s > Winter { return fmt.Sprintf("Season(%d)", int(s)) } return seasons[s-1] }String()method adds the ability to printSeasonconstants as astringrather than anint, so for the statementfmt.Println(Spring)we getspringinstead of1. For variables outside the rangeSpring..Winter, we printSeason(X), whereXis an integer value of theSeasontype variable. - Create
IsValid()methodfunc (s Season) IsValid() bool { switch s { case Spring, Summer, Autumn, Winter: return true } return false }IsValid()is a helper method that validates whether theSeasontype variable is one of the valuesSpring,Summer,Autumn, orWinter. This function should always be called for user input, to verify that the user has not passed an invalid value of theenum. main()functionfunc main() { mySeasons := []Season{Spring, Summer, Autumn, Winter, 6} for _, s := range mySeasons { fmt.Printf("season: %s, is valid: %t\n", s, s.IsValid()) } }The
main()function presents what you get when you use theString()andIsValid()methods for defined values ofSeasonconstants as well as values out of range.
