Основы Go
2026-02-26 18:09 Diff

Срезы (slices) — это основной способ работы с коллекциями переменной длины в Go. В отличие от массивов, срезы не фиксируют количество элементов в типе и позволяют изменять длину коллекции. По сути это аналог списков из других языков.

Технически срез — это обёртка над массивом. Он хранит в себе три вещи:

  • Указатель на первый элемент.
  • Текущую длину (len()).
  • Вместимость (cap()) — максимальное количество элементов, которые можно хранить, не выделяя новый массив.

Объявление среза

Объявление среза аналогично массиву, с одним исключением. Внутри скобок ничего не указывается, так как длина не известна и может меняться.

Значением по умолчанию в таком определении nums будет nil.

Срез можно создать напрямую с помощью литерала. В этом случае указывается тип элементов, а длина выводится из количества значений:

Можно создать пустой срез, добавив в конце {}:

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

Такая запись (с добавлением {} в конце) полезна, когда нужно объявить срез заранее, но значения будут добавлены позже (например, в цикле или через append).

Создание среза через make

Функция make() используется для создания срезов с заранее заданной длиной и (опционально) вместимостью. Это бывает полезно для предварительного выделения памяти и улучшения производительности.

Если вы знаете, что срез будет расширяться, но хотите избежать лишних перераспределений памяти, можно задать вместимость больше длины:

Это позволяет избежать постоянного выделения новой памяти при каждом append() — важно в циклах или при работе с большими объёмами данных. С другой стороны, подобные оптимизации нужны далеко не всегда и заниматься ими имеет смысл только тогда, когда эта часть кода уже стала узким горлышком.

Добавление элементов

Для добавления элементов к срезу используется встроенная функция append(). Она возвращает новый срез с добавленными элементами:

Если вместимость позволяет, append() просто дописывает значение. Если нет — Go выделяет новый массив с увеличенной вместимостью.

Можно добавить сразу несколько элементов:

Можно объединять срезы:

Это удобно, например, при объединении результатов нескольких запросов или списков.

Доступ к элементам

Срезы поддерживают доступ по индексу. В этом смысле поведение идентично массивам:

Если обратиться по индексу, которого нет в срезе — будет ошибка во время выполнения (runtime panic), а не на этапе компиляции.

Поэтому перед обращением стоит проверять длину через len().

Передача и возврат из функции

Когда срез передаётся в функцию, копируется только небольшой «заголовок» среза: длина, вместимость и указатель на общий подлежащий массив. Сами элементы не копируются. Поэтому если внутри функции менять элементы по индексу (s[i] = ...), изменения видны и снаружи. Это остаётся передачей по значению (копируется заголовок), просто в нём хранится указатель на данные.

Также можно возвращать срез из функции:

Подробнее о нюансах append() при работе внутри функций (и почему иногда изменения «не выходят» наружу) узнаем в одном из следующих уроков.

Пример: заполнение среза в цикле и возврат

Сравнение с массивами