Go: значения, типы, константы и iota
Go — статически типизированный язык: у каждого выражения есть тип, а операция допустима только для совместимых типов. Переменная хранит значение, имеет объявленный тип и всегда получает zero value до явного присваивания. Константа вычисляется во время компиляции и не является переменной; нетипизированная константа получает конкретный тип лишь там, где контекст этого требует.
Зачем это на интервью
Эта тема проверяет способность предсказывать поведение без запуска: что выведет код, почему var не равен :=, где переполнение обнаружится, чем type ID string лучше alias и что в действительности делает iota.
Минимум для E4
- Различать объявление
var, короткое объявление:=, явный тип и вывод типа. - Называть zero values:
0,false,"",nilдля ссылочных представлений и нулевое значение структуры. - Отличать определённый тип (
type UserID string) от alias (type UserID = string). - Объяснять, что
iota— счётчик строкconst-блока, начинающийся с нуля.
package main
import "fmt"
type UserID string // новый определённый тип
type LegacyID = string // alias: это тот же string
const (
Read = 1 << iota // 1
Write // 2: повторяется выражение
Admin // 4
)
func main() {
var count int // 0
name := "Ada" // string, только внутри функции
const limit = 1 << 60 // нетипизированная целая константа
fmt.Println(count, name, Read|Write, limit)
}:= разрешён только в функции и требует хотя бы одну новую переменную в текущей области видимости. В x, err := f() переменная err может быть уже объявлена, но хотя бы x должна быть новой.
Углубление для E5/Senior
Zero value — часть API-контракта. Хороший тип либо безопасно полезен без конструктора, либо явно запрещает некорректное состояние методами и документацией. Например, sync.Mutex годен сразу, но map в нулевом состоянии можно читать и удалять из него, однако запись вызовет panic. Не стоит проектировать API, в котором «нулевое значение» тихо означает опасный production-режим.
Нетипизированные числовые константы обладают произвольной точностью в рамках спецификации и могут быть присвоены представимому типу. Это удобно для time.Second * 3, но выражение становится ошибкой, когда значение не представимо: var x int8 = 128. Преобразование значения во время выполнения, напротив, может изменить его при переполнении согласно правилам целевого типа; проверку диапазона надо писать явно на границах протокола.
iota подходит для локальных стабильных наборов флагов. Он не создаёт enum: это константы без проверки исчерпывающего switch, без сериализационного контракта и без защиты от перестановки значений. Значения, хранимые в БД или передаваемые по сети, назначают явно; строковое представление и валидация должны быть сознательными.
Ключевые понятия
- Объявленный и базовый тип — определённый тип имеет собственную идентичность даже при том же базовом типе.
- Assignability — правило, по которому значение можно присвоить без явного преобразования; одинаковое представление не делает типы взаимозаменяемыми.
- Преобразование —
T(x)создаёт значение типаT; это не всегда проверка и не всегда безопасная операция. - Zero value — значение, которое получает память переменной до инициализации.
- Константа — неизменяемое compile-time выражение базового типа boolean, numeric или string.
- Нетипизированная константа — константа, чей конкретный тип выбирает контекст.
iota— предопределённый идентификатор, номер текущей спецификации в одномconst (...).
Типовые вопросы
- Почему
type ID stringне то же, чтоtype ID = string?- Первое вводит новый тип и защищает от случайного смешения с
string; второе лишь даёт второе имя существующему типу.
- Первое вводит новый тип и защищает от случайного смешения с
- Можно ли написать в nil map?
- Нет, присваивание вызывает panic. Чтение даёт zero value,
deleteбезопасен, а для записи map надо создать черезmakeили литерал.
- Нет, присваивание вызывает panic. Чтение даёт zero value,
- Что делает
iotaпосле пустой строки константы?- Счётчик увеличивается, а отсутствующее выражение повторяет предыдущее непустое выражение вместе с новым значением
iota.
- Счётчик увеличивается, а отсутствующее выражение повторяет предыдущее непустое выражение вместе с новым значением
- Почему
const x = 1 << 100компилируется?- Это нетипизированная константа; ошибка появится, если присвоить её типу, который не может представить значение.
- Когда
:=не компилируется?- Вне функции или если в текущей области ни одна переменная не новая; тогда используйте
=либо новое имя.
- Вне функции или если в текущей области ни одна переменная не новая; тогда используйте
- Всегда ли conversion проверяет переполнение?
- Нет. Преобразование числового значения следует правилам представления целевого типа; диапазон следует валидировать отдельно.
Практика
- Определите
type Currency stringи константы трёх валют. Готово: функция валидации отвергает неизвестное значение, аCurrencyнельзя неявно передать туда, где нуженstring. - Реализуйте набор битовых прав через
iota. Готово: есть методыHasиString, тесты для комбинаций и документировано, что значения не меняются после релиза. - Напишите тесты для nil
map, nilsliceи zero value структуры. Готово: отдельно проверены чтение, append, запись и ожидаемый panic там, где он предусмотрен.
Частые ошибки и ловушки
- Считать, что все zero values одинаково пригодны к мутации: nil map нельзя заполнять.
- Использовать
iotaдля публичного wire-формата и затем переставлять строки. - Смешивать alias и новый тип, теряя защиту доменного значения.
- Не замечать shadowing при
:=во вложенном блоке. - Полагаться на неявное преобразование разных числовых типов: Go требует явности.
Связанные темы
Go · Основы языка Go · Массивы, срезы, map и строки · Функции, указатели и семантика значений