In this post, we will learn about the “iota” identifier in Go, how it’s used, and when not to use it.
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
:
- The value of iota increments with each constant declaration
- 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.