In this post, we will learn about the “iota” identifier in Go, how it’s used, and when not to use it.

banner

The “iota” identifier is used to represent integer based constants in Go, and is a convenient way to declare a sequence of constants, while keeping the code readable.

If you just want to see the code, you can view it on Github or run it on the Go playground

Using Iota for Constant Declaration

Normally, constants can be declared by specifying its value, for example:

const (
	SPADES   = 1
	HEARTS   = 2
	DIAMONDS = 3
	CLUBS    = 4
)

func main() {
	fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
}

Output:

SPADES: 1 HEARTS: 2 DIAMONDS: 3 CLUBS: 4

However, we can use iota and avoid explicitly declaring the incremental integer values:

const (
	SPADES   = iota
	HEARTS   = iota
	DIAMONDS = iota
	CLUBS    = iota
)

func main() {
	fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
}

Output:

SPADES: 0 HEARTS: 1 DIAMONDS: 2 CLUBS: 3

From this example, we can observe two properties of iota:

  1. The value of iota increments with each constant declaration
  2. It starts from 0

We can also omit iota declarations after the first one, so this code would give the same result:

const (
	SPADES = iota
	HEARTS   
	DIAMONDS 
	CLUBS    
)

Starting From Non-Zero Indexes

As we saw in the previous example, iota starts from 0, but we can change the base value by adding the given quantity to the first declaration.

So, if we want the same result as the first example, where SPADES = 1, then we can declare our constants as follows:

const (
  // Now, the index starts from 1
	SPADES = iota + 1
	HEARTS
	DIAMONDS
	CLUBS
)

Output:

SPADES: 1 HEARTS: 2 DIAMONDS: 3 CLUBS: 4

In general, if we want to start from an index n, we can start the constant declaration with iota + <n>

Skipping Values

Remember, iota values increment for every constant declaration. So, if we want to skip a value, we can declare an empty constant value:

const (
	SPADES = iota
	HEARTS
	_ // The value of iota will increase, since this is still a constant declaration
	DIAMONDS
	CLUBS
)

Output:

SPADES: 0 HEARTS: 1 DIAMONDS: 3 CLUBS: 4

You can add as many empty constant declarations as you want to skip a number of values ahead.

Custom Math Operations

Although the value of iota increases by one incrementally, we can make use of basic math operations to modify the final value assigned to each constant.

For example, if we use iota * 10, it will give the n’th multiple of ten for each const declaration:

const (
	ZERO = 10 * iota // = 10 * 0
	TEN // = 10 * 1
	TWENTY // = 10 * 2
	THIRTY // = 10 * 3
)

func main() {
	fmt.Println("ZERO:", ZERO, "TEN:", TEN, "TWENTY:", TWENTY, "THIRTY:", THIRTY)
}

Output:

ZERO: 0 TEN: 10 TWENTY: 20 THIRTY: 30

Similarly, we can use the shift-left operation to make exponential increments:

const (
	BYTE     = 1 << (iota * 10) // = 1 << 0
	KILOBYTE                    // = 1 << 10
	MEGABYTE                    // = 1 << 20
	GIGABYTE                    // = 1 << 30
)

func main() {
	fmt.Println("BYTE:", BYTE, "KILOBYTE:", KILOBYTE, "MEGABYTE:", MEGABYTE, "GIGABYTE:", GIGABYTE)
}

Output:

BYTE: 1 KILOBYTE: 1024 MEGABYTE: 1048576 GIGABYTE: 1073741824

Resetting the Iota Value

The iota value increments within a constant declaration block only. So, once we declare another group of constants in a different declaration block, the iota value will reset.

For example, we can declare all the constants we discussed in the previous examples in the same file:

const (
	SPADES = iota
	HEARTS
	DIAMONDS
	CLUBS
)

// iota resets here
const (
	ZERO = 10 * iota
	TEN
	TWENTY
	THIRTY
)

// iota resets here
const (
	BYTE     = 1 << (iota * 10) // = 1 << 0
	KILOBYTE                    // = 1 << 10
	MEGABYTE                    // = 1 << 20
	GIGABYTE                    // = 1 << 30
)

func main() {
	fmt.Println("SPADES:", SPADES, "HEARTS:", HEARTS, "DIAMONDS:", DIAMONDS, "CLUBS:", CLUBS)
	fmt.Println("ZERO:", ZERO, "TEN:", TEN, "TWENTY:", TWENTY, "THIRTY:", THIRTY)
	fmt.Println("BYTE:", BYTE, "KILOBYTE:", KILOBYTE, "MEGABYTE:", MEGABYTE, "GIGABYTE:", GIGABYTE)
}

Output:

SPADES: 0 HEARTS: 1 DIAMONDS: 3 CLUBS: 4
ZERO: 0 TEN: 10 TWENTY: 20 THIRTY: 30
BYTE: 1 KILOBYTE: 1024 MEGABYTE: 1048576 GIGABYTE: 1073741824

Why Is Iota Useful

Using iota is a more readable and idiomatic way to declare arbitrary constants or enums.

Using iota, we can add more constant values without thinking about the next numerical value to assign.

It also helps us avoid certain types of errors in our code.

To illustrate, imagine we had a large list of error codes to declare as constants. In these cases, its easy to make the human error of assigning the same value twice:

const (
	ERR_ABC = 1
	ERR_DEF = 2
	ERR_HIJ = 3
	ERR_KLM = 3 // oops
	ERR_NOP = 4
	ERR_QRS = 5
)

If we used iota to assign these values instead, we wouldn’t have this problem, and our code would be less error prone.

When to Avoid Iota

Iota is best used when there are arbitrary, or incremental values to be used as constants.

In some cases, there isn’t any sequential relationship between constants. For example, time units like second, minute, hour and day can be represented as non-sequential constants:

const (
	SECOND = 1
	MINUTE = 60 * SECOND
	HOUR   = 60 * MINUTE
	DAY    = 24 * HOUR
)

In this case, there is no way we can use iota because the relationship between successive constants does not increment by the same quantity.

Another case where we should not use iota is when the numerical values are not arbitrary. For example, representing the total cards in a deck, or total number of jokers:

const (
  TOTAL_CARDS = 52
  TOTAL_JOKERS = 2
)

In this case, the values of the constants are meaningful, and not sequential, so it’s better to not use iota.

You can view the complete code for all the examples above on Github or run it on the Go playground.