ПрограммированиеBackend разработчик

Опишите особенности работы с slices в Go: что такое nil slice, какая разница между length и capacity, и как правильно увеличивать slice без утечки данных или паники?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Slice — это динамический массив, основная структурная единица при работе с массивами в Go. Исторически языки программирования предлагали фиксированные массивы или более тяжёлые структуры. Go реализовал slices как удобный инструмент для обработки коллекций памяти с автоматическим управлением её размером и возможностью изменять длину.

Проблема заключается в правильном управлении памятью, обработке граничных случаев (пустые, nil, полные слайсы), а также в понимании разницы между длиной и ёмкостью слайса.

Решение — правильно использовать встроенные функции len(), cap(), и оперировать срезами по соглашениям Go.

Пример кода:

var a []int // nil slice, len=0, cap=0 b := make([]int, 0) // пустой slice, len=0, cap=0 c := make([]int, 3, 5) // len=3, cap=5 c = append(c, 4, 5, 6) // cap увеличится автоматически

Ключевые особенности:

  • Nil slice (var s []int) не выделяет память вообще, отличается от пустого слайса (make([]int, 0)) только внутренне.
  • Length показывает текущее количество элементов, capacity — максимальное без дополнительной аллокации.
  • При append, если capacity недостаточно, создаётся новый под капотом, а старые данные остаются без изменений.

Вопросы с подвохом.

Какой длины и ёмкости будет slice после append к nil slice?

Nil slice (var s []int) после append(s, 1) становится слайсом длины 1, ёмкости 1 — Go сам выделяет хранилище.

var s []int s = append(s, 42) // s теперь [42], len=1, cap=1

Можно ли обращаться к capacity слайса, равного nil?

Да, у nil slice обе функции — len и cap — возвращают 0. Паники не будет.

var s []int fmt.Println(len(s), cap(s)) // 0 0

Что произойдет при попытке присвоить элементу по индексу без предварительной аллокации памяти слайсу, равному nil?

Паника index out of range, потому что у слайса нет элементов.

var s []int s[0] = 1 // panic: runtime error: index out of range

Типовые ошибки и анти-паттерны

  • Ошибки при передаче nil slice или пустого слайса в функцию, где требуется определённая длина.
  • Перезаполнение слайса без учёта роста capacity и нелогично написанные циклы с append.
  • Попытки присваивания по индексу вместо append для добавления элементов.

Пример из жизни

Негативный кейс

Команда решила в сервере Go везде использовать только var s []T, ожидая, что это всегда эквивалентно make([]T, 0). В результате некоторые json-маршализации возвращали null, а не [].

Плюсы:

  • Меньше аллокаций памяти на старте.

Минусы:

  • Некорректная работа JSON (в результате приходил null вместо массива).
  • Runtime ошибки при обращении по индексу.

Позитивный кейс

Использование make([]T, 0, 100) для preallocation, а далее — только append в цикле. Так минимизируются аллокации памяти и часто выигрывается по производительности.

Плюсы:

  • Эффективная работа с памятью.
  • Нет panics и неожиданных значений при маршализации.

Минусы:

  • Возможен перерасход памяти, если ожидаемое число элементов не совпадает с заданной capacity.