В Go переменные, объявленные без явного инициализатора, автоматически получают так называемые нулевые значения (zero values), отличающиеся для разных типов:
int — 0bool — falsestring — ""Это удобно (отсутствие мусора в памяти), но может быть опасно: для некоторых типов, например, срезов и карт, работа с nil-значением может привести к панике или багам.
Пример:
var m map[string]int m["key"] = 1 // panic: assignment to entry in nil map var s []int fmt.Println(s, s == nil) // [] true (можно добавлять элементы, но нельзя обращаться по индексу)
Чем отличается nil-значение среза от nil-значения карты/канала, и к каким багам это приводит?
Правильный ответ: Срез с nil-значением можно передавать в функции, использовать в append и range — это безопасно, но работа с nil-картой приведет к панике при попытке записи (но чтение допускается и вернет zero value).
Пример:
var m map[string]int fmt.Println(m["no_key"]) // 0 — безопасно m["key"] = 1 // panic! var s []int s = append(s, 42) // ок
История
Описание: В крупном проекте использовали неинициализированные мапы для агрегации данных. При первой записи приложение стабильно падало с panic. Проблема обнаружилась на проде, когда ввели новую статистику.
История
Описание: Сервис сериализовал неинициализированный срез, отправляя его как JSON в API-ответ. Вместо
[]клиенты получалиnull— пришлось менять схему на фронте, чтобы обрабатывать оба варианта.
История
Описание: В модуле кеширования некорректно сравнивали nil map с пустым map и из-за этого некорректно определяли: есть ли элементы. Это приводило к лишним обращениям к базе данных, снижая производительность.