Перейти к содержимому
Теория · Переменные и типы · урок 8 из 10

Константы и iota

В уроке о переменных константа была коробкой, которую запечатали ещё до запуска программы. Представьте теперь целый ряд таких коробок на полке. На каждую нужно наклеить номер по порядку: 0, 1, 2. Клеить номера вручную долго, а если вставить новую коробку в середину, придётся переклеить все номера после неё.

Константы часто идут именно такой группой: уровни сложности, дни недели, цвета. Go нумерует такие константы сам:

GO
package main

import "fmt"

func main() {
    const (
        low = iota
        mid
        high
    )
    fmt.Println(low, mid, high) // 0 1 2
}

Программа печатает 0 1 2: три константы получили номера по порядку. При этом = стоит только в первой строке, да и там вместо числа написано iota. Давайте разберём, как это работает.

Блок const

Несколько констант объявляют одним блоком. Он устроен так:

  • ключевое слово const, а за ним список констант в круглых скобках, по одной на строку;
  • блок пишут и внутри функции, как здесь, и над ней;
  • строка блока без = повторяет выражение предыдущей строки.

Повторяется не готовое значение, а сама запись. Это правило понадобится нам, когда дойдём до iota.

Константы с типом и без

Константу можно объявить без типа или с типом. Ведут они себя по-разному.

Без типа: const limit = 10

Такая константа не имеет типа и подстраивается под выражение, в котором стоит. Поэтому она работает с разными типами.

При const rate = 2 компилируются и rate * 3, и rate * 1.5. Переменной rate := 2 во втором выражении понадобилось бы преобразование.

С типом: const limit int = 10

Запись читается слева направо:

  • const — ключевое слово: «объявляю константу»;
  • limit — имя;
  • int — тип;
  • = 10 — значение.

Такая константа ведёт себя как значение типа int. Поэтому limit * 1.5 уже не скомпилируется: 1.5 (untyped float constant) truncated to int. Тип указывают, когда константа должна оставаться строго int.

Счётчик iota

iota — счётчик строк внутри блока const. В первой строке блока он равен 0 и растёт на единицу с каждой следующей строкой. В первом примере это выглядит так:

  1. low = iota — на первой строке iota равен 0, и low получает 0.
  2. mid повторяет запись iota уже на второй строке и получает 1.
  3. high повторяет её на третьей строке и получает 2.

Новая строка, вставленная в середину блока, сдвигает номера всех констант после неё без правок. В каждом новом блоке const счёт снова начинается с 0.

Сдвиг и пропуск

Счётчик можно сдвинуть выражением, а ненужное значение пропустить с помощью пустого имени _:

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

Давайте пройдём блок по строкам:

  1. first = iota + 1 — iota равен 0, и first получает 0 + 1, то есть 1.
  2. second повторяет запись iota + 1 при iota, равном 1, и получает 2.
  3. _ — iota равен 2, а получившееся значение 3 отбрасывается.
  4. fourth — iota равен 3, и fourth получает 3 + 1, то есть 4.

Нажмите «Следующий шаг» и следите за iota: он растёт на каждой строке блока, и на строке с _ тоже.

Вживуюiota по строкам блокашаг 1 из 7

Начинается блок const: компилятор считает его до запуска, и iota стоит на 0.

Следите, как iota растёт на каждой строке блока

Выражение с iota

Выражение вроде iota + 1 ставят в первую строку блока, и следующие строки повторяют его целиком. Так нумерацию начинают не с 0, а с 1.

Ловушка здесь та же, что у любой строки без =: повторяется запись, а не готовое значение. Поэтому second получает не 1, а iota + 1 при новом iota, то есть 2.

Пустое имя _

Строка из одного _ пропускает номер, который не нужен. Константа на ней не создаётся.

Здесь картинка с наклейками перестаёт работать. Наклейки клеят только на коробки, а iota считает строки блока. Строка с _ не создаёт константу, но номер на ней всё равно расходуется.

На собеседовании любят показать блок с iota и _ и спросить, что напечатает программа. Ответ дают два правила этого урока:

  • строка без = повторяет запись;
  • _ тоже занимает строку.

Частая ошибка

Константе задали результат вызова функции:

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

Значение константы должно быть известно при компиляции, а функция выполняется только во время работы программы. Результат вызова хранят в переменной: sum := add(1, 2).

Главное

  • Строка блока const без = повторяет запись предыдущей строки, а не её значение.
  • iota равен номеру строки в блоке, начиная с 0; каждый новый блок начинает счёт заново.
  • Строка с _ тоже считается: iota на ней растёт, а значение отбрасывается.
  • Значение константы известно при компиляции, поэтому результат вызова функции в неё положить нельзя.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом