Как ведет себя нулевой (nil) слайс в Go и какие операции с ним допустимы?

Ответ

Нулевой (nil) слайс — это слайс, который не указывает ни на какой базовый массив. Он является значением по умолчанию для типа []T.

var s []int // s является nil-слайсом

Несмотря на то, что он nil, он безопасен и полезен для многих операций.

Допустимые операции

  • Получение длины и ёмкости: len(s) и cap(s) для nil-слайса всегда возвращают 0.
  • Добавление элементов: Операция append прекрасно работает с nil-слайсом. При первом вызове append будет выделен новый базовый массив, и слайс перестанет быть nil.
    var s []int
    s = append(s, 1) // Работает корректно
  • Использование в цикле for range: Цикл по nil-слайсу просто не выполнит ни одной итерации, не вызывая ошибок.
    for _, v := range s { // Безопасно, цикл не начнется
        // ...
    }
  • Сравнение с nil: s == nil вернет true.

Недопустимые операции

  • Доступ по индексу: Попытка получить или изменить элемент по индексу (s[0] = 1 или _ = s[0]) вызовет панику panic: runtime error: index out of range, так как длина слайса равна нулю.

Отличие от пустого (не-nil) слайса

Важно отличать nil-слайс от пустого слайса, который указывает на существующий (хоть и пустой) массив.

var nilSlice []int             // nilSlice == nil -> true
emptySlice := make([]int, 0) // emptySlice == nil -> false
emptySlice2 := []int{}       // emptySlice2 == nil -> false

Ключевое преимущество: В большинстве случаев nil-слайс ведет себя так же, как и пустой (в len, cap, append, range). Поэтому в Go идиоматично не инициализировать пустые слайсы специально (make([]T, 0)), а просто объявлять их через var, что делает код чище и не требует лишних аллокаций.