Constants and iota
In the lesson on variables, a constant was a box sealed before the program even starts. Now picture a whole row of such boxes on a shelf. Each one needs a number sticker in order: 0, 1, 2. Sticking numbers by hand takes time, and if you insert a new box in the middle, you have to re-stick every number after it.
Constants often come in exactly such groups: difficulty levels, days of the week, colors. Go numbers such constants itself:
package main
import "fmt"
func main() {
const (
low = iota
mid
high
)
fmt.Println(low, mid, high) // 0 1 2
}The program prints 0 1 2: the three constants got numbers in order. Yet only the first line has an =, and even there it is iota rather than a number. Let's see how that works.
The const block
Several constants are declared in one block. It is built like this:
- the keyword
const, followed by a list of constants in parentheses, one per line; - the block can be written inside a function, as here, or above it;
- a line of the block without
=repeats the expression of the previous line.
What repeats is not the computed value but the expression itself. We will need this rule when we get to iota.
Constants with and without a type
A constant can be declared without a type or with one. The two behave differently.
Without a type: const limit = 10
Such a constant has no type and adapts to the expression it appears in. That is why it works with different types.
With const rate = 2, both rate * 3 and rate * 1.5 compile. A variable rate := 2 would need a conversion in the second expression.
With a type: const limit int = 10
The line reads left to right:
constis the keyword: "I'm declaring a constant";limitis the name;intis the type;= 10is the value.
Such a constant behaves like a value of type int. So limit * 1.5 no longer compiles: 1.5 (untyped float constant) truncated to int. You give the type when the constant must stay strictly an int.
The iota counter
iota is a line counter inside a const block. On the first line of the block it equals 0, and it grows by one with each following line. In the first example it goes like this:
low = iota: on the first lineiotaequals0, andlowgets0.midrepeats the expressioniotaon the second line and gets1.highrepeats it on the third line and gets2.
A new line inserted in the middle of the block shifts the numbers of all constants after it with no edits. In every new const block the count starts again from 0.
Shifting and skipping
The counter can be shifted by an expression, and an unneeded value can be skipped with the blank identifier _:
const (
first = iota + 1
second
_
fourth
)
fmt.Println(first, second, fourth) // 1 2 4Let's walk through the block line by line:
first = iota + 1:iotaequals0, andfirstgets0 + 1, that is,1.secondrepeats the expressioniota + 1withiotaequal to1and gets2._:iotaequals2, and the resulting value3is thrown away.fourth:iotaequals3, andfourthgets3 + 1, that is,4.
Press "Next step" and watch iota: it grows on every line of the block, the _ line included.
1–4package main · import "fmt"5func main() {6 const (7 first = iota + 18 second9 _10 fourth11 )12 fmt.Println(first, second, fourth)13}
A const block starts: the compiler works it out before the run, and iota is at 0.
An expression with iota
An expression such as iota + 1 goes on the first line of the block, and the following lines repeat it whole. That way the numbering starts from 1 rather than 0.
The trap here is the same as for any line without =: the expression repeats, not the computed value. So second gets not 1 but iota + 1 with the new iota, that is, 2.
The blank identifier _
A line holding only _ skips a number you don't need. No constant is created on it.
Here the sticker picture stops working. Stickers go only on boxes, but iota counts the lines of the block. The line with _ creates no constant, yet it still uses up a number.
Interviewers like to show a block with iota and _ and ask what the program prints. Two rules from this lesson give the answer:
- a line without
=repeats the expression; _takes up a line too.
Common mistake
A constant is given the result of a function call:
package main
import "fmt"
func add(a, b int) int {
return a + b
}
func main() {
const sum = add(1, 2) // ✗
fmt.Println(sum)
}add(1, 2) (value of type int) is not constantA constant's value must be known at compile time, but a function runs only while the program is running. The result of a call is kept in a variable: sum := add(1, 2).
Key points
- A line of a
constblock without=repeats the expression of the previous line, not its value. iotaequals the line number in the block, starting from0; every new block starts the count again.- A line with
_counts too:iotagrows on it, and the value is thrown away. - A constant's value is known at compile time, so the result of a function call cannot go into it.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.