Константы и iota
В уроке о переменных константа была коробкой, которую запечатали ещё до запуска программы. Представьте теперь целый ряд таких коробок на полке. На каждую нужно наклеить номер по порядку: 0, 1, 2. Клеить номера вручную долго, а если вставить новую коробку в середину, придётся переклеить все номера после неё.
Константы часто идут именно такой группой: уровни сложности, дни недели, цвета. 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 и растёт на единицу с каждой следующей строкой. В первом примере это выглядит так:
low = iota— на первой строкеiotaравен0, иlowполучает0.midповторяет записьiotaуже на второй строке и получает1.highповторяет её на третьей строке и получает2.
Новая строка, вставленная в середину блока, сдвигает номера всех констант после неё без правок. В каждом новом блоке const счёт снова начинается с 0.
Сдвиг и пропуск
Счётчик можно сдвинуть выражением, а ненужное значение пропустить с помощью пустого имени _:
const (
first = iota + 1
second
_
fourth
)
fmt.Println(first, second, fourth) // 1 2 4Давайте пройдём блок по строкам:
first = iota + 1—iotaравен0, иfirstполучает0 + 1, то есть1.secondповторяет записьiota + 1приiota, равном1, и получает2._—iotaравен2, а получившееся значение3отбрасывается.fourth—iotaравен3, иfourthполучает3 + 1, то есть4.
Нажмите «Следующий шаг» и следите за iota: он растёт на каждой строке блока, и на строке с _ тоже.
1–4package main · import "fmt"5func main() {6 const (7 first = iota + 18 second9 _10 fourth11 )12 fmt.Println(first, second, fourth)13}
Начинается блок const: компилятор считает его до запуска, и iota стоит на 0.
Выражение с iota
Выражение вроде iota + 1 ставят в первую строку блока, и следующие строки повторяют его целиком. Так нумерацию начинают не с 0, а с 1.
Ловушка здесь та же, что у любой строки без =: повторяется запись, а не готовое значение. Поэтому second получает не 1, а iota + 1 при новом iota, то есть 2.
Пустое имя _
Строка из одного _ пропускает номер, который не нужен. Константа на ней не создаётся.
Здесь картинка с наклейками перестаёт работать. Наклейки клеят только на коробки, а iota считает строки блока. Строка с _ не создаёт константу, но номер на ней всё равно расходуется.
На собеседовании любят показать блок с iota и _ и спросить, что напечатает программа. Ответ дают два правила этого урока:
- строка без
=повторяет запись; _тоже занимает строку.
Частая ошибка
Константе задали результат вызова функции:
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 constantЗначение константы должно быть известно при компиляции, а функция выполняется только во время работы программы. Результат вызова хранят в переменной: sum := add(1, 2).
Главное
- Строка блока
constбез=повторяет запись предыдущей строки, а не её значение. iotaравен номеру строки в блоке, начиная с0; каждый новый блок начинает счёт заново.- Строка с
_тоже считается:iotaна ней растёт, а значение отбрасывается. - Значение константы известно при компиляции, поэтому результат вызова функции в неё положить нельзя.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.