Спрогнозируйте вывод программы и объясните, в какой момент увеличивается значение iota:
package main
import "fmt"
const (
A = iota
B
C = 10
D
E = iota
)
func main() {
fmt.Println(A, B, C, D, E)
}
Программа напечатает 0 1 10 10 4. Идентификатор iota получает индекс текущего объявления константы внутри блока const, начиная с нуля. Он увеличивается для каждого объявления, но не подставляется автоматически в последующие строки, если выражение константы явно задано или повторено по правилу неявного выражения.
В Go нет отдельного ключевого слова для перечислений. Связку const и iota используют, чтобы компактно задавать последовательности констант: состояния, флаги, уровни или индексы полей.
Такой подход уменьшает количество повторяющегося кода, но требует отличать индекс объявления от выражения, которое вычисляет значение константы.
В блоке находятся пять объявлений констант. Ошибка возникает, если считать, что iota всегда равен предыдущему значению плюс один или что он автоматически участвует в вычислении каждой следующей константы.
Неверное понимание особенно опасно для кодов протокола и битовых флагов: визуально соседние строки могут иметь последовательные индексы, но фактические значения могут содержать пропуски или повторения.
iota равен позиции текущего ConstSpec в блоке const, начиная с 0:
A = iota: позиция 0, поэтому A == 0;B: выражение не указано, поэтому повторяется предыдущее выражение iota, но уже с позицией 1; B == 1;C = 10: позиция равна 2, однако явно заданное выражение даёт C == 10;D: повторяется именно предыдущее выражение 10, поэтому D == 10, а не 11;E = iota: позиция объявления равна 4, поэтому E == 4.Таким образом, индекс iota и значение константы — разные сущности. Позиция продолжает увеличиваться даже в строках с явными значениями, но результатом становится только вычисление текущего выражения.
Минимальный пример последовательного объявления:
Здесь неявное выражение iota повторяется для каждой строки, поэтому значения равны 0, 1, 2 и 3. Если между строками появится явное выражение, последующие строки без выражения будут повторять уже его, а не исходный шаблон.
Блоки const независимы: при начале нового блока iota снова равен нулю. Тип константы также может быть задан явно, например State = int(iota), либо оставаться нетипизированным до момента использования.
В API нужно задать состояния заказа: New, Paid, Shipped и Cancelled. Для них можно использовать последовательный iota, что удобно и компактно, но числовые значения нельзя бездумно сохранять как стабильный внешний формат: добавление строки в середину изменит значения последующих состояний.
Альтернатива — явно назначить значения каждой константе. Это увеличивает объём кода, зато сохраняет совместимость с базой данных, логами или сетевым протоколом при перестановке объявлений.
Если значения являются только внутренними временными состояниями, выбран последовательный iota: он проще и снижает вероятность ручной ошибки. Для публичного или сохраняемого формата выбраны явные числа, потому что стабильность значения важнее краткости объявления.
Что произойдёт, если после C = 10 написать D без выражения?
D получит 10, потому что отсутствие выражения означает повторение полного предыдущего выражения, а не автоматическое увеличение предыдущего результата. В данном случае повторяется 10, тогда как индекс iota внутри блока продолжает становиться равным 3.
Сбрасывается ли iota после явного значения или после пустой строки?
Нет. iota не сбрасывается ни из-за явного выражения, ни из-за форматирования или пустых строк. Он сбрасывается только при начале нового блока const; в пределах блока его значение определяется позицией объявления.
Можно ли использовать iota для битовых флагов без изменения выражения?
Нет, для флагов обычно нужен явный шаблон, например 1 << iota. Если написать только iota, значения будут последовательными числами 0, 1, 2, 3, а не степенями двойки. Для флагов также часто начинают с 1 << iota, чтобы нулевое значение означало отсутствие всех флагов.