В чём разница между zero value для map и slice в Go?

Ответ

Ключевое различие в том, как nil-значения map и slice ведут себя при операциях записи.

Хотя zero value для обоих типов — это nil, их поведение кардинально отличается.

Zero Value для map

  • Значение: nil.
  • Поведение: Попытка записи в nil-map вызовет панику (panic: assignment to a nil map). Чтение, вызов len или range для nil-map безопасны и не вызовут панику (вернут zero value для типа значения и 0 соответственно).
  • Инициализация: Перед использованием map для записи её необходимо явно инициализировать с помощью make() или литерала.
var m map[string]int // m is nil

fmt.Println(m["key"]) // Безопасно, вернет 0
fmt.Println(len(m))   // Безопасно, вернет 0

m["key"] = 1          // !!! panic: assignment to a nil map

// Правильная инициализация
m = make(map[string]int)
m["key"] = 1 // OK

Zero Value для slice

  • Значение: nil.
  • Поведение: nil-слайс можно безопасно использовать со встроенной функцией append. При первом вызове append для nil-слайса Go автоматически выделит память под новый нижележащий массив. Однако обращение к элементу по индексу вызовет панику.
  • Инициализация: Явная инициализация перед append не требуется.
var s []int // s is nil, len(s) == 0, cap(s) == 0

// Безопасно: Go выделит память и добавит элемент
s = append(s, 42)

fmt.Println(s)      // Выведет [42]
fmt.Println(len(s)) // Выведет 1

// А вот так делать нельзя
var s2 []int // s2 is nil
s2[0] = 100  // !!! panic: runtime error: index out of range

Сравнительная таблица

Операция var m map[T]T (nil map) var s []T (nil slice)
Чтение (v := m[k]) ✅ Безопасно ❌ Паника (по индексу)
len() ✅ Безопасно (возвращает 0) ✅ Безопасно (возвращает 0)
Запись (m[k] = v) Паника ❌ Паника (по индексу)
append() (неприменимо) Безопасно

Ответ 18+ 🔞

А, слушай, тут один нюанс, который вгоняет в ступор, как будто тебе на голову упал кирпич, но ты не понял, откуда. Речь про nil-овые map и slice.

Вот смотри, оба они по дефолту — nil, ну, как и положено. Но ведут себя, как два разных мудака на одной вечеринке. Один молча в углу сидит, а второй внезапно начинает орать и бить посуду.

Нулевая map — это пидарас шерстяной

  • Что такое: nil.
  • Как себя ведёт: Попробуй в неё что-то записать — получишь пиздец, прямую панику в лицо: panic: assignment to a nil map. А вот читать из неё, мерять длину или итерироваться — пожалуйста, на здоровье, вернёт нули и молча сдохнет.
  • Что делать: Перед тем как совать в неё данные, её надо родить через make() или литерал. Иначе — в рот меня чих-пых.
var m map[string]int // m — это нихуя, nil

fmt.Println(m["key"]) // Ну ок, прочитал 0, похуй
fmt.Println(len(m))   // 0, всё чинно

m["key"] = 1          // !!! ЁБАНА В РОТ! ПАНИКА! assignment to a nil map

// Делай по-человечески
m = make(map[string]int)
m["key"] = 1 // Теперь всё охуенно

Нулевой slice — хитрая жопа

  • Что такое: Тоже nil.
  • Как себя ведёт: А вот это уже интереснее. Этого уёбка можно безопасно кормить функцией append. Первый раз, когда ты сунешь в него элемент, Go сам, по-тихому, выделит память под массив. Но! Если ты попробуешь ткнуть в него пальцем по индексу — опять пиздец, паника.
  • Что делать: С append можно даже не париться, он сам всё сделает. Главное — не лезь по индексу в пустой слайс.
var s []int // s — нихуя, nil, len=0, cap=0

// А вот так можно, он сам внутри всё создаст
s = append(s, 42)

fmt.Println(s)      // [42], красота
fmt.Println(len(s)) // 1, всё пучком

// А вот так — НИЗЯ!
var s2 []int // s2 — нихуя
s2[0] = 100  // !!! ОПЯТЬ ПАНИКА! index out of range

Короче, таблица, чтобы не ебал мозг

Действие var m map[T]T (nil мапа) var s []T (nil слайс)
Прочитать (v := m[k]) ✅ Можно, вернёт 0 ❌ Паника (если по индексу)
len() ✅ Можно, вернёт 0 ✅ Можно, вернёт 0
Записать (m[k] = v) ПИЗДЕЦ, ПАНИКА ❌ Паника (если по индексу)
append() (не пришьёшь к жопе) МОЖНО, ОН САМ ВСЁ СДЕЛАЕТ

Вот и вся разница. Один — истеричка, с которым надо церемониться. Второй — пофигист, который работает через append. Запомни и не путай, а то runtime тебе ебальник набьёт.