iota — Create Effective Constants in Golang
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.