Как получить первые n элементов слайса в Go
Что проверяют
Синтаксис s[:n] знают все, поэтому вопрос на самом деле про устройство слайса: общий массив, длину и ёмкость. Следом почти всегда спрашивают, что будет после append в результат и когда код упадёт с паникой.
Как отвечать
Базовый ответ s[:n], но сразу оговорите границы. Паника slice bounds out of range возникает, если n отрицательный или больше ёмкости. Если n больше длины, но не больше cap(s), паники нет: вы молча получите элементы из резерва массива за пределами длины. Поэтому ограничивать нужно явно, через встроенную min(n, len(s)) (есть с Go 1.21).
Дальше главное:
- общий массив:
s[:n]не копирует данные. Новый заголовок указывает на тот же массив, и запись вhead[0]видна в исходном слайсе; - ловушка с
append: уhead := s[:2]ёмкость как уs, поэтомуappend(head, x)перезапишетs[2], а не выделит новую память; - полное выражение среза:
s[:n:n]ограничивает ёмкость, и первый жеappendскопирует данные в новый массив; - честная копия:
slices.Clone(s[:n])из пакетаslicesилиmakeплюсcopy, если результат живёт дольше исходных данных; - удержание памяти: маленький срез от огромного слайса держит в памяти весь массив. Если из буфера на мегабайты нужны первые десять элементов, скопируйте их.
Итоговая функция: проверить n < 0, взять n = min(n, len(s)), вернуть s[:n:n] или копию, в зависимости от того, кто дальше владеет данными. Про nil: s[:0] от непустого слайса даёт пустой, но не nil слайс, и json.Marshal выведет [], а не null.
Где спотыкаются
Уверенно говорят, что s[:n] при n > len(s) всегда паникует. Граница проверяется по ёмкости, и ошибка получается тихой.
Возвращают s[:n] из функции, а вызывающий код делает append и портит исходные данные. Такие баги ищут днями.
Не думают об удержании памяти: хранят в долгоживущей структуре крошечный срез от большого буфера и удивляются росту потребления.