iota — Create Effective Constants in Golang

Bala subramaniyan
The Startup
Published in
4 min readSep 6, 2020

Some concepts have names, and sometimes we care about those names, even (or especially) in our code.

At other times, we only care to distinguish one thing from the other. There are times when there’s no inherently meaningful value for a thing. For example, if we’re storing products in a database table we probably don’t want to store their category as a string. We don’t care how the categories are named, and besides, marketing changes the names all the time.

We care only that they’re distinct from each other.

const (
CategoryBooks = 0
CategoryHealth = 1
CategoryClothing = 2
)

Instead of 0, 1, and 2 we could have chosen 17, 43, and 61. The values are arbitrary.Constants are important but they can be hard to reason about and difficult to maintain.

iota

iota makes it easy to declare sequentially growing numeric constants in Go:

const (
Low = iota
Medium
High
)
fmt.Printf("Low: %d\nMedium: %d\nHigh: %d\n", Low, Medium, High)

iota sets the value of Low to 0 and instructs the compiler that the following constants have increasing numeric values.

Low: 0
Medium: 1
High: 2

Operations with iota

It’s possible to make operation when using iotas

package mainimport "fmt"const (
_ = iota
a = iota + 10
b int = iota * 10
c float64 = iota + 5.1
)func main() {
fmt.Printf("%v — %T\n", a, a)
fmt.Printf("%v — %T\n", b, b)
fmt.Printf("%v — %T\n", c, c)
}
Output=>
11 — int
20 — int
8.1 — float64

Resetting iota increment

Every time a keyword Const appears in the code, iota’s increment is reset.

package main
import "fmt"
const (
a = iota
b int = iota
)
const (
c = iota
)
func main() {
fmt.Printf("%v — %T\n", a, a)
fmt.Printf("%v — %T\n", b, b)
fmt.Printf("%v — %T\n", c, c)
}
Output=>
0 — int
1 — int
0 — int

Creating bitmask values with iota

Iota can be very useful when creating a bitmask. For instance, to represent the state of a network connection which may be secure, authenticated, and/or ready, we might create a bitmask like the following:

const (
Secure = 1 << iota // 0b001
Authn // 0b010
Ready // 0b100
)
// 0b011: Connection is secure and authenticated, but not yet Ready
ConnState := Secure | Authn

fmt.Printf(`Secure: 0x%x (0b%03b)
Authn: 0x%x (0b%03b)
ConnState: 0x%x (0b%03b)
`, Secure, Secure, Authn, Authn, ConnState, ConnState)
Output =>
Secure: 0x1 (0b001)
Authn: 0x2 (0b010)
ConnState: 0x3 (0b011)

Skipping values

The value of iota is still incremented for every entry in a constant list even if iota is not used:

const ( // iota is reset to 0
a = 1 << iota // a == 1
b = 1 << iota // b == 2
c = 3 // c == 3 (iota is not used but still
incremented)
d = 1 << iota // d == 8
)
fmt.Printf("a: %d, b: %d, c: %d, d: %d\n", a, b, c, d)
Output=>
a: 1, b: 2, c: 3, d: 8

It will also be incremented even if no constant is created at all, meaning the empty identifier can be used to skip values entirely:

const (
a = iota // a = 0
_ // iota is incremented to 1
b // b = 2
)
fmt.Printf("a: %d, b: %d\n", a, b)
Output=>
a: 0, b: 2

Using iota in an expression list

Because iota is incremented after each ConstSpec, values within the same expression list will have the same value for iota:

const (
bit0, mask0 = 1 << iota, 1<<iota - 1 //bit0 == 1, mask0 == 0
bit1, mask1 //bit1 == 2, mask1 == 1
_, _ //skips iota == 2
bit3, mask3 //bit3 == 8, mask3 == 7
)
fmt.Printf("bit0: %d, mask0: 0x%x\n", bit0, mask0)
fmt.Printf("bit3: %d, mask3: 0x%x\n", bit3, mask3)
Output=>
bit0: 1, mask0: 0x0
bit3: 8, mask3: 0x7

Using iota in an expression

iota can be used in expressions, so it can also be used to assign values other than simple incrementing integers starting from zero. To create constants for SI units, use this example from Effective Go:

type ByteSize int

const (
_ = iota //ignore first value by assigning to
//blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
)
fmt.Printf("KB: %d\n", KB)
Output=>
KB: 1024

Emulating enums

Go doesn’t have a syntax for enumerations, but you can emulate it with constants and a naming scheme.

Consider a C++ enum:

enum {
tagHtml,
tagBody,
taDiv
};

In Go you can do it as:

const (
tagBody = iota
tagDiv
tagHr
)

This declares 3 untyped, numeric constants. iota starts numbering of constants. tagBody has value of 0, tagDiv a value of 1 etc. This is convenient way to assign unique numeric values.

If the values matter, you can assign them explicitly:

const (
one = 1
five = 5
seven = 7
)

Adding type safety

In the above example tagBody etc. is an untyped constant so it can be assigned to any numeric type.

We can define a unique type for our enum:

type HTMLTag intconst (
tagBody HTMLTag = iota
tagDiv
tagHr
)

Conclusion

I am leaving you with this, to build upon and get hands on experience.

That’s it! I hope this short post about Go iota was. useful! Of course, all comments are welcome.

--

--