Learn Go Programming

Visual, concise and detailed tutorials, tips and tricks about Go (aka Golang).

Follow publication

★ Ultimate Visual Guide to Go Enums and iota

Go enums and iota — Full of tips and tricks with runnable code examples.

Inanc Gumus
Learn Go Programming
10 min readOct 19, 2017

Important: In this tutorial, you’ll learn how to use the enums without iota in the first section — After that, you’ll also learn about iota and the other helper tools as well after the first section. So, learn to crawl first, then walk then run!

What is an Enum? ★

An enum groups related constants together in one type.

Example enums:

  • Timezones: EST, CST…
  • T-shirt Sizes: Small, Medium, Large
  • Server Statuses: Unknown, Running, Stopped, Resumed

Why do we need enums?

  • Grouping and expecting only some related values
  • Sharing common behavior
  • Avoids using invalid values
  • To increase the code readability and the maintainability

How can you imitate an enum in Go?

For example, suppose that you want to create an enum for the weekdays.

Each constant will have the same type: Weekday

★ First: Declare a new custom type: Weekday

Weekday will unify our enum constants under a common type.

type Weekday int

★ Second: Declare related constants for Weekday

Assign them different numeric values, so, they don’t clash.

const (
Sunday Weekday = 0
Monday Weekday = 1
Tuesday Weekday = 2
Wednesday Weekday = 3
Thursday Weekday = 4
Friday Weekday = 5
Saturday Weekday = 6
)
fmt.Println(Sunday) // prints 0fmt.Println(Saturday) // prints 6

Creating common behavior for Weekday enum

You attach methods to a type to define its behaviors.

The attached methods will be the inseparable parts of Weekday, and shared among Weekday constants.

String() method:

func (day Weekday) String() string {
// declare an array of strings
// ... operator counts how many
// items in the array (7)
names := [...]string{
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"}
// → `day`: It's one of the
// values of Weekday constants.
// If the constant is
Sunday,
// then
day is 0.
//
// prevent panicking in case of
// `day` is out of range of Weekday
if day < Sunday || day > Saturday {
return "Unknown"
}
// return the name of a Weekday
// constant from the names array
// above.
return names[day]
}

Let’s try:

fmt.Printf("Which day it is? %s\n", Sunday)
//
Which day it is? Sunday
We’re done adding String() method. Play with the code to understand it better.

Weekend() method:

func (day Weekday) Weekend() bool {
switch day {
case Sunday, Saturday: // If day is a weekend day
return true
default: // If day is not a weekend day
return false
}
}

Convention: Instead of IsWeekend, we just use Weekend.

Let’s try:

fmt.Printf("Is Saturday a weekend day? %t\n", Saturday.Weekend())
//
Is Saturday a weekend day? true
We attached the Weekend method to Weekday type. Go try it.

Creating names automatically

You can use Rob Pike’s Stringer to create String method automatically.

# install the stringer tool
go get -u -a golang.org/x/tools/cmd/stringer
# grab the code from here
# and save it in weekday.go
# inside your $GOPATH/src/enums
# then, run stringer to create
# enum names automatically
stringer -type Weekday weekdays.go

Automate enum values with: iota

→ A numeric universal counter starting at 0

→ Used only with constant declarations

→ Can be shadowed. Please, don’t do that.

How to use iota?

Iota expression is repeated by the other constants until another assignment or type declaration shows up.

Each constant’s type will be Weekday and each will execute their own iota + 1.
To see how iota behaves be sure to try the example.

Let’s see how iota ticks tocks

iota increases by 1 after each line except empty and comment lines.

When not to use iota

const (
RestartMarkerReply = 110
ServiceReadyInNMinutes = 120
CommandOK = 200
CommandNotImplemented = 202
// ...
)

Don’t use iota for a list of predefined values like FTP server status codes.

const (
Fatal = iota
)

Just assign 0 there. It’s harmless but it’s unnecessary either.

This section will enhance your understanding of iota.

A basic expression

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota)

// iota: 1, CST: -6
CST

// iota: 2, MST: -7
MST

// iota: 3, MST: -8
PST
)
You can use any constant expression with iota.

Resetting iota

// iota reset: it will be 0.
const
(
Zero = iota // Zero = 0
One // One = 1
)
// iota reset: will be 0 again
const
(
Two = iota // Two = 0
)
// iota: reset
const Three = iota // Three = 0
Whenever a const keyword appears iota resets to zero.

Skipping some values

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota)
// _ is the blank identifier
//
iota: 1
_
// iota: 2, MST: -7
MST
// iota: 3, MST: -8
PST
)
You can skip some lines with a blank identifier, but, iota will keep increasing.

About iota’s behavior on comment and empty lines

type Timezone intconst (
// iota: 0, EST: -5
EST Timezone = -(5 + iota)
// On a comment or an empty
// line, iota will not
// increase
// iota: 1, CST: -6
CST
// iota: 2, MST: -7
MST
// iota: 3, MST: -8
PST
)
iota will not increase on comments or empty lines.

Using iota in the middle

const (
One = 1
Two = 2
// Three = 2 + 1 => 3
// iota in the middle
Three = iota + 1
// Four = 3 + 1 => 4
Four
)
Iota can be used on any line inside a const declaration. Here it‘s 3 instead of 0.

Multiple iotas in a single line

const (
// Active = 0, Moving = 0,
// Running = 0

Active, Moving, Running = iota, iota, iota
// Passive = 1, Stopped = 1,
// Stale = 1

Passive, Stopped, Stale
)

In the same line, all constants will get the same iota values.

const (
// Active = 0, Running = 100
Active, Running = iota, iota + 100
// Passive = 1, Stopped = 101
Passive, Stopped
// You can't declare like this.
// The last expression will be
// repeated
CantDeclare
// But, you can reset
// the last expression
Reset = iota
// You can use any other
// expression even without iota
AnyOther = 10
)
By using new expressions you can stop the previously repeating expression.

Repeating and canceling expressions

const (
// iota: 0, One: 1 (type: int64)
One int64 = iota + 1
// iota: 1, Two: 2 (type: int64)
// Two will be declared as if:
// Two int64 = iota + 1
Two
// iota: 2, Four: 4 (type: int32)
Four int32 = iota + 2
// iota: 3, Five: 5 (type: int32)
// Five will be declared as if:
// Five int32 = iota + 2
Five
// (type: int)
Six = 6
// (type: int)
// Seven will be declared as if:
// Seven = 6
Seven
)

The last used expression and type will be repeated.

You can group constants with different expressions.

Evens and odds

type Even boolconst (
// 0 % 2 == 0 ==> Even(true)
a = Even(iota % 2 == 0)
// 1 % 2 == 0 ==> Even(false)
b
// 2 % 2 == 0 ==> Even(true)
c
// 3 % 2 == 0 ==> Even(false)
d
)
`==` operator converts the iota expression to bool type.

Counting backward

const (
max = 10
)
const (
a = (max - iota) // 10
b // 9
c // 8
)

Go stdlib text scanner also uses this pattern.

Producing Alphabets

const (
// string will convert the
// expression into string.
//
// or, it'll assign character
// codes.
a = string(iota + 'a') // a
b // b
c // c
d // d
e // e
)

Bitwise operations

type Month intconst (
// 1 << 0 ==> 1
January Month = 1 << iota
February // 1 << 1 ==> 2
March // 1 << 2 ==> 4
April // 1 << 3 ==> 8
May // 1 << 4 ==> 16
June // ...
July
August
September
October
November
December
// Break the iota chain here. // AllMonths will have only
// the assigned month values,
// not the iota's.
AllMonths = January | February |
March | April | May | June |
July | August | September |
October | November |
December
)

With the powers of 2, this code creates an all emcompassing constant from the previous constants.

You don’t need to use iota for every constant in an enum, you can overwrite the values manually.

Beware the zero-value

type Activity intconst (
Sleeping = iota
Walking
Running
)
func main() {
var activity Activity
// activity initialized to
// its zero-value of int
// which is
Sleeping
}

0 is a zero-value for integers. So, you can’t know whether the Activity is initialized or not; Is it really in the Sleeping state?

The code that uses zero-based iota values.

iota + 1 trick

const (
Sleeping = iota + 1
Walking
Running
)
func main() {
var activity Activity
// activity will be zero,
// so it's not initialized
activity = Sleeping
// now you know that it's been
// initialized

}

Use “iota + 1” to be sure that the enum type is initialized.

The code that uses one-based iota values.

Unknown state pattern

const (
Unknown = iota
Sleeping
Walking
Running
)

Start with an “Unknown” to be sure of enum’s initialization.

The code that uses a default Unknown value.

▶︎ In the Greek alphabet, iota is the ninth and the smallest letter (in terms of its visual representation). Pronounced like this. It represents a very small quantity of something.

In vector math, and in some programming languages, it’s been used to generate an array of consecutive integers as in 1, 2, 3, 4… ⟧

If you’re curious just like me, you can read the implementation of constants, and iota in Go source code. Iota is declared here as const iota = 0. And, the constant implementation is here, and the behavior of incrementing iota is here.

Iota was in Iverson’s book: A Programming Language (APL) from 1962.

Iota borrowed from APL by one of the creators of Go, Ken Thompson.

A discussion to make iota work with variables in Go 2.

Alright, that’s all for now. Thank you for reading so far.

Let’s stay in touch:

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in Learn Go Programming

Visual, concise and detailed tutorials, tips and tricks about Go (aka Golang).

Written by Inanc Gumus

Coder. Gopher. Maker. Stoic. Since 1992.

Responses (7)

Write a response