Table of Contents
Introduction to golang enum
Enum(enumerators) is a set of named constant values that allows developers to create constant values that have useful names. In other words , an enum groups related constants together into in type. In this article we will dive into what enums in Go and ways of defining an enum and its use cases.
Examples of enums.
- Months : January, February, March, April …
- Days of a week : Monday, Tuesday ….
- Campus Direction: North, South , East, West
Many programming languages have the concept of enumerations, where you can specify that a type can only have a limited set of values. Go doesn’t have an enumeration type. Instead, it has iota, which lets you assign an increasing value to a set of constants.
Given the above examples, it's now clear that enums are sets of constants that are known and limited. Enums are needed because :
- They prevent developers from using invalid values.
- They are used in grouping constants that have related values
- They are used to define constants that share common behavior
- They increase code readability and maintainability
Defining constants
Let us define a set of related constant variables in Go. In this example , we are defining days of the week because they are a fixed set of 7 values. That is Monday, Tuesday , Wednesday , Thursday , Friday , Saturday and Sunday.
Example
package main
import "fmt"
const (
Monday = 0
Tuesday = 1
Wednesday = 2
Thursday = 3
Friday = 4
Saturday = 5
Sunday = 6
)
func main() {
fmt.Println(Monday)
fmt.Println(Tuesday)
fmt.Println(Wednesday)
fmt.Println(Thursday)
fmt.Println(Friday)
fmt.Println(Saturday)
fmt.Println(Sunday)
}
Output
$ go run main.go
0
1
2
3
4
5
6
Define enums using iota
Iota is an identifier in Go that is used with constants that help simplify constant definition. This prevents developers from writing values manually which has proven to be error prone. It defines constants by auto-incrementing the numbers. It represents successive integer values like 0, 1,2,3,4,5 …
When defining custom enum types follow the below steps:
- Declare a new custom type e.g
campusDirection
(int type) - Declare constant as related using iota
Example
package main
import "fmt"
type campusDirection int64
const (
North campusDirection = iota // 0
South = iota // 1
East = iota // 2
West = iota // 3
)
func main() {
fmt.Println(North)
fmt.Println(South)
fmt.Println(East)
fmt.Println(West)
}
Explanation
In the above example , we define a custom type campusDirection
of type int64
. We also define a set of constant values, North
, South
,East
and West
of type compusDirection
. For each constant, we assign an iota
keyword. Each constant will have a value assigned to from 0 upto to the last one. Therefore North=0, South = 1, East =2 and South= 3.It is worth mentioning that each constant is of type campusDirection
and each executes their own iota + 1
. The iota increases by 1 after each line except empty and comment lines
Output
$ go run main.go
0
1
2
3
This example can be rewritten without assigning all constant the iota
keyword to every constant. Instead the first constant gets assigned the iota keyword and the rest of the constant will be assigned successive constant values.
Example
package main
import "fmt"
type CampusDirection int64
const (
North CampusDirection = iota // 0
South // 1
East // 2
West // 3
)
func main() {
fmt.Println(North) // 0
fmt.Println(South) // 1
fmt.Println(East) // 2
fmt.Println(West) // 3
}
Creating enums starting with 1
By default, iota
assigns constant values starting from 0. This default behavior can be changed by adding a value that you want the iota to start with.
Example
package main
import "fmt"
type CampusDirection int64
const (
North CampusDirection = iota + 1 // 1
South // 2
East = iota + 5 // 7
West // 8
)
func main() {
fmt.Println(North)
fmt.Println(South)
fmt.Println(East)
fmt.Println(West)
}
Explanation
In the above example, we use the iota + int
value syntax to define the initial value of an iota. In this example, we set the initial value for the set of constant values to 1 using North campusDirection = iota + 1
. We also go ahead and increase the iota value by 5 using the East = iota + 5
syntax.
Output
$ go run main.go
1
2
7
8
Creating enums by multiplying
The initial value of enums can be changed by multiplying the iota with an integer. In this example we will demonstrate multiplication with iota.
Example
package main
import "fmt"
type campusDirection int64
const (
North campusDirection = iota + 1 // 0 + 1 = 1
South = iota * 1000 // 1 * 1000 = 1000
East // 2 * 1000 = 2000
West // 3 * 1000 = 3000
)
func main() {
fmt.Println(North)
fmt.Println(South)
fmt.Println(East)
fmt.Println(West)
}
Output
$ go run main.go
1
1000
2000
3000
Resetting iota
An enum can be reset back to initial value zero by re-decalaring iota more than once in the code.
Example
package main
import "fmt"
type campusDirection int64
const (
North campusDirection = iota + 1000 // 0 + 1000 = 1000
)
// Resetting constants
const (
South campusDirection = iota // 0
East // 1
West // 2
)
func main() {
fmt.Println(North)
fmt.Println(South)
fmt.Println(East)
fmt.Println(West)
}
Explanation
In the above example, we define constants using iota two times. In the first constant definition, the North constant of type campusDirection
has its value set to 1000 (iota + 1000
).
Next, we define another set of constant values of type campusDirection
starting from the iota initial value of 0
. This resets the iota value from 1000
to 0
.
Output
go run main.go
1000
0
1
2
Skipping values in a list of constants
Upto this point we know how to define successive constants using iota. It is also possible to skip a value assigned by the iota using the blank identifier.
Example
package main
import "fmt"
type campusDirection int64
const (
North campusDirection = iota + 1 // 0 + 1 = 1
_ // skipping 2
South // 3
East // 4
West // 5
)
func main() {
fmt.Println(North)
fmt.Println(South)
fmt.Println(East)
fmt.Println(West)
}
Explanation
In the above example, we define campus direction constant values using enums. In the list of constants we add a blank identifier after the North constant variable.
This blank identifier (_
) will be skipped when the iota will be assigning values to the campus direction constants. In our example, number 2 is skipped in assignment of cosntants.
Output
$ go run main.go
1
3
4
5
Defining common behavior
Since we have our own custom type defined (campusDirection
), we can add a receiver function to it so that all the constants have the same behavior. Let us define a function called toString()
that converts the int values assigned by iota to string representation.
Example
package main
import "fmt"
type campusDirection int64
func (c campusDirection) toString(index campusDirection) string {
return []string{"North", "East", "West", "South"}[index]
}
const (
North campusDirection = iota + 1 // 1
South // 2
East // 3
West // 4
)
func main() {
var direction = North
n := direction.toString(0)
e := direction.toString(1)
w := direction.toString(2)
s := direction.toString(3)
fmt.Println(n)
fmt.Println(e)
fmt.Println(w)
fmt.Println(s)
}
Explanation
In the above example, we define a receiver function on the campusDirection
type. This function is called toString()
and it takes the index int of the constant that you want to access. In the main function, we declare a direction variable of type campusDirection. We access individual direction using the toString() method on the campusDirection instance(direction)
Output
$ go run main.go
North
East
West
South
When not to use iota
Do not use iota for a list of constants that are predefined. These constants can be for example HTTP status codes. The below code sample is NOT acceptable.
Example
package main
import "fmt"
type statusCode int64
const (
statusOK statusCode = iota
statusCreated = 201
serverError = 500
NotFound = 404
)
Summary
This article covers working with enums in Go. It covers enum definitions, using iota , setting initial values for iota ,resetting iota , skipping values in iota and when not to use iota.
References
In the example code for “Creating enums by multiplying” there is a line ” East // 2 * 1000 = 4000″.
I’m new to `go`, but this looks… odd.
-E
Thanks for highlighting the typo. In the next line the output was correct but in the comments 4000 was wrongly written instead of 2000.