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 увеличится автоматически
Ключевые особенности:
var s []int) не выделяет память вообще, отличается от пустого слайса (make([]int, 0)) только внутренне.Какой длины и ёмкости будет 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
Команда решила в сервере Go везде использовать только var s []T, ожидая, что это всегда эквивалентно make([]T, 0). В результате некоторые json-маршализации возвращали null, а не [].
Плюсы:
Минусы:
Использование make([]T, 0, 100) для preallocation, а далее — только append в цикле. Так минимизируются аллокации памяти и часто выигрывается по производительности.
Плюсы:
Минусы: