Константы и iota в Go: пошаговая инструкция
Разбираемся, как объявлять константы в Go, зачем нужна iota, как она считает и где ломает ожидания. К концу инструкции у вас будут работающие перечисления, битовые маски и понимание, когда iota не стоит использовать.

Что понадобится до начала
Инструкция написана для Go 1.21+, но всё описанное работает начиная с Go 1.0 — механизм констант и iota не менялся с первой версии языка. Убедитесь, что Go установлен и доступен из терминала:
go version
# go version go1.22.4 linux/amd64 — или ваша версия
Любой редактор подойдёт: VS Code с расширением Go, GoLand, даже nano. Все примеры запускаются через go run main.go без внешних зависимостей.
Шаг 1. Объявление простых констант
Константа в Go — значение, известное на этапе компиляции. Оно не может быть изменено после объявления.
package main
import "fmt"
const Pi = 3.14159
const Greeting = "Привет"
func main() {
fmt.Println(Pi) // 3.14159
fmt.Println(Greeting) // Привет
}
Проверка: запустите go run main.go. В терминале появятся две строки — число и приветствие. Если попытаться присвоить Pi = 2.0 внутри main, компилятор выдаст ошибку cannot assign to Pi.
Типизированные и нетипизированные константы
Константа без явного типа — нетипизированная. Она ведёт себя как литерал и подстраивается под контекст:
const Limit = 100 // нетипизированная, может быть int, int64, float64…
const TypedLimit int32 = 100 // типизированная, всегда int32
На практике нетипизированные константы удобнее: они не требуют приведения типов при передаче в функции, ожидающие разные числовые типы.
Шаг 2. Группировка констант в блок
Несколько связанных констант удобно собирать в один блок const():
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
Проверка: добавьте вывод через fmt.Println — числа должны совпасть. Пока всё просто, но именно внутри такого блока работает iota.
Шаг 3. Знакомство с iota
iota — встроенный счётчик, который начинается с нуля и увеличивается на единицу для каждой следующей строки внутри блока const(). Не для каждой константы в строке, а именно для каждой строки.
const (
Red = iota // 0
Green // 1 — выражение iota повторяется неявно
Blue // 2
)
func main() {
fmt.Println(Red, Green, Blue) // 0 1 2
}
Проверка: вывод 0 1 2. Обратите внимание — для Green и Blue выражение = iota не написано. Go повторяет последнее заданное выражение для каждой следующей строки блока. Это ключевое правило, и оно же — источник большинства ошибок.
iota — не переменная и не функция. Это предопределённый идентификатор, который существует только внутри блока const и сбрасывается в ноль в каждом новом блоке.
Шаг 4. Перечисления с пропуском нулевого значения
Нулевое значение в Go — значение по умолчанию для любого типа. Если Red = 0, то переменная типа int, которую забыли инициализировать, случайно станет «красной». Стандартный приём — пропустить ноль:
type Color int
const (
_ Color = iota // 0 — пропускаем через пустой идентификатор
Red // 1
Green // 2
Blue // 3
)
Проверка: fmt.Println(Red, Green, Blue) выводит 1 2 3. Переменная var c Color без инициализации равна 0, что не совпадает ни с одним цветом — ошибку легко поймать.
Шаг 5. Выражения с iota: сдвиги и арифметика
iota подставляется в произвольное выражение. Классический пример — битовые флаги:
type Permission int
const (
Read Permission = 1 << iota // 1 << 0 = 1
Write // 1 << 1 = 2
Execute // 1 << 2 = 4
)
Проверка: fmt.Println(Read, Write, Execute) → 1 2 4. Комбинировать флаги можно через побитовое ИЛИ: Read | Write даст 3.
Единицы измерения: байты в кибибайтах
const (
_ = iota
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
)
Проверка: fmt.Println(KB, MB, GB) → 1024 1048576 1073741824.
Шаг 6. Несколько констант в одной строке
iota увеличивается за строку, а не за имя. Если в одной строке объявлено несколько констант, они получают одинаковое значение iota:
const (
A, B = iota, iota + 10 // A=0, B=10
C, D // C=1, D=11
)
Проверка: fmt.Println(A, B, C, D) → 0 10 1 11. Приём редкий, но полезно знать, чтобы не споткнуться при чтении чужого кода.
Шаг 7. Метод String() — превращаем числа в имена
Перечисление без строкового представления неудобно при отладке. Добавьте метод String() к вашему типу:
type Direction int
const (
North Direction = iota
East
South
West
)
func (d Direction) String() string {
names := [...]string{"North", "East", "South", "West"}
if d < North || d > West {
return "Unknown"
}
return names[d]
}
func main() {
fmt.Println(South) // South, а не 2
}
Проверка: вывод South. Для больших перечислений удобнее генерировать метод автоматически через go generate и утилиту stringer из пакета golang.org/x/tools.
Если не получилось: типичные ошибки
| Симптом | Причина | Решение |
|---|---|---|
cannot assign to Pi | Попытка изменить константу | Используйте переменную (var), если значение должно меняться |
| Все константы в блоке равны нулю | Забыли написать = iota в первой строке блока | Первая строка блока должна содержать выражение с iota |
| iota не сбросилась | Константы в одном блоке const(), а ожидали новый отсчёт | Начните новый блок const() — iota обнуляется в каждом блоке |
constant overflows int | Выражение вроде 1 << (10 * iota) при iota=7 даёт число больше int | Используйте uint64 или уберите лишние строки |
Неожиданное значение при пропуске строки через _ | iota всё равно увеличивается для пропущенной строки | Помните: _ «съедает» значение, но счётчик растёт |
Что делать дальше
Константы и iota — фундамент, но не потолок:
- Попробуйте
stringer— автоматическая генерация методаString()для перечислений. Документация в репозиторииgolang.org/x/tools. - Разберите, как стандартная библиотека использует iota: пакеты
os(флаги файлов),net/http(статусы),time(единицы длительности) — хорошие примеры. - Если перечисление перерастает десять значений и требует валидации, маршалинга в JSON и хранения в базе — посмотрите на кодогенерацию, чтобы не поддерживать всё это вручную.
Замеряйте не перформанс — константы бесплатны, они подставляются при компиляции, — а читаемость. Если коллега не может за пять секунд понять, какое значение у пятой строки в блоке, выражение с iota слишком хитрое и его стоит упростить.
Сноска. iota — предопределённый идентификатор в Go, представляющий целочисленный счётчик строк внутри блока const. Название заимствовано из языка APL, где iota обозначала генерацию последовательности целых чисел.
Разрушитель авто-разводов: Фото заказ-наряда СТО: завышения, дубли работ, лишние позиции.
Спросить за 15 ₽Источники: The Go Programming Language Specification — Constant declarations, iota, Go Blog — Constants (Rob Pike)
Частые вопросы
Можно ли использовать iota вне блока const?
Сбрасывается ли iota между разными блоками const?
Как пропустить конкретное значение в середине перечисления?
Что если я добавлю новую константу в середину списка — сломается ли существующий код?
Работает ли iota со строковыми константами?
Что будет, если у меня другая версия Go — например, 1.18?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.