Skip to content
Theory · Variables and types · lesson 8 of 10

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:

GO
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:

  • const is the keyword: "I'm declaring a constant";
  • limit is the name;
  • int is the type;
  • = 10 is 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:

  1. low = iota: on the first line iota equals 0, and low gets 0.
  2. mid repeats the expression iota on the second line and gets 1.
  3. high repeats it on the third line and gets 2.

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 _:

GO
const (
    first = iota + 1
    second
    _
    fourth
)
fmt.Println(first, second, fourth) // 1 2 4

Let's walk through the block line by line:

  1. first = iota + 1: iota equals 0, and first gets 0 + 1, that is, 1.
  2. second repeats the expression iota + 1 with iota equal to 1 and gets 2.
  3. _: iota equals 2, and the resulting value 3 is thrown away.
  4. fourth: iota equals 3, and fourth gets 3 + 1, that is, 4.

Press "Next step" and watch iota: it grows on every line of the block, the _ line included.

Liveiota, line by linestep 1 of 7

A const block starts: the compiler works it out before the run, and iota is at 0.

Watch iota grow on every line of the block

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:

GO
package main

import "fmt"

func add(a, b int) int {
    return a + b
}

func main() {
    const sum = add(1, 2) // ✗
    fmt.Println(sum)
}
TEXT
add(1, 2) (value of type int) is not constant

A 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 const block without = repeats the expression of the previous line, not its value.
  • iota equals the line number in the block, starting from 0; every new block starts the count again.
  • A line with _ counts too: iota grows 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.

Sign in another way