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 useiota
in 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
enum
type Season int const ( Spring Season = iota + 1 Summer Autumn Winter )
As a first step, we declare new integer type
Season
and create successive constant values using theiota
keyword.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
int
values is faster than comparing two strings. Constants of typeint
also take less memory space.Why do we declare the custom type
Season
instead 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 = d
When using
Season
type, to make this code run without any error, you need to explicitly convert thed
variable, so it is not possible to do it by accident:var d int = 6 season := Spring season = Season(d)
Also, a custom type of
enum
makes 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
enum
elements 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 printSeason
constants as astring
rather than anint
, so for the statementfmt.Println(Spring)
we getspring
instead of1
. For variables outside the rangeSpring..Winter
, we printSeason(X)
, whereX
is an integer value of theSeason
type 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 theSeason
type 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 ofSeason
constants as well as values out of range.