0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Срезы (slices) - это основной способ работы с коллекциями переменной длины в Go. В отличие от массивов, срезы не фиксируют количество элементов в типе и позволяют изменять длину коллекции. По сути это аналог списков из других языков.</p>
1
<p>Срезы (slices) - это основной способ работы с коллекциями переменной длины в Go. В отличие от массивов, срезы не фиксируют количество элементов в типе и позволяют изменять длину коллекции. По сути это аналог списков из других языков.</p>
2
<p>Технически срез - это обёртка над массивом. Он хранит в себе три вещи:</p>
2
<p>Технически срез - это обёртка над массивом. Он хранит в себе три вещи:</p>
3
<ul><li>Указатель на первый элемент.</li>
3
<ul><li>Указатель на первый элемент.</li>
4
<li>Текущую длину (len()).</li>
4
<li>Текущую длину (len()).</li>
5
<li>Вместимость (cap()) - максимальное количество элементов, которые можно хранить, не выделяя новый массив.</li>
5
<li>Вместимость (cap()) - максимальное количество элементов, которые можно хранить, не выделяя новый массив.</li>
6
</ul><h2>Объявление среза</h2>
6
</ul><h2>Объявление среза</h2>
7
<p>Объявление среза аналогично массиву, с одним исключением. Внутри скобок ничего не указывается, так как длина не известна и может меняться.</p>
7
<p>Объявление среза аналогично массиву, с одним исключением. Внутри скобок ничего не указывается, так как длина не известна и может меняться.</p>
8
<p>Значением по умолчанию в таком определении nums будет nil.</p>
8
<p>Значением по умолчанию в таком определении nums будет nil.</p>
9
<p>Срез можно создать напрямую с помощью литерала. В этом случае указывается тип элементов, а длина выводится из количества значений:</p>
9
<p>Срез можно создать напрямую с помощью литерала. В этом случае указывается тип элементов, а длина выводится из количества значений:</p>
10
<p>Можно создать пустой срез, добавив в конце {}:</p>
10
<p>Можно создать пустой срез, добавив в конце {}:</p>
11
<p>Пустой срез - это не nil, но он ведёт себя как пустая коллекция. Он готов к использованию и не вызывает ошибок при чтении длины, прохождении в цикле и других типичных операциях.</p>
11
<p>Пустой срез - это не nil, но он ведёт себя как пустая коллекция. Он готов к использованию и не вызывает ошибок при чтении длины, прохождении в цикле и других типичных операциях.</p>
12
<p>Такая запись (с добавлением {} в конце) полезна, когда нужно объявить срез заранее, но значения будут добавлены позже (например, в цикле или через append).</p>
12
<p>Такая запись (с добавлением {} в конце) полезна, когда нужно объявить срез заранее, но значения будут добавлены позже (например, в цикле или через append).</p>
13
<h2>Создание среза через make</h2>
13
<h2>Создание среза через make</h2>
14
<p>Функция make() используется для создания срезов с заранее заданной длиной и (опционально) вместимостью. Это бывает полезно для предварительного выделения памяти и улучшения производительности.</p>
14
<p>Функция make() используется для создания срезов с заранее заданной длиной и (опционально) вместимостью. Это бывает полезно для предварительного выделения памяти и улучшения производительности.</p>
15
<p>Если вы знаете, что срез будет расширяться, но хотите избежать лишних перераспределений памяти, можно задать вместимость больше длины:</p>
15
<p>Если вы знаете, что срез будет расширяться, но хотите избежать лишних перераспределений памяти, можно задать вместимость больше длины:</p>
16
<p>Это позволяет избежать постоянного выделения новой памяти при каждом append() - важно в циклах или при работе с большими объёмами данных. С другой стороны, подобные оптимизации нужны далеко не всегда и заниматься ими имеет смысл только тогда, когда эта часть кода уже стала узким горлышком.</p>
16
<p>Это позволяет избежать постоянного выделения новой памяти при каждом append() - важно в циклах или при работе с большими объёмами данных. С другой стороны, подобные оптимизации нужны далеко не всегда и заниматься ими имеет смысл только тогда, когда эта часть кода уже стала узким горлышком.</p>
17
<h2>Добавление элементов</h2>
17
<h2>Добавление элементов</h2>
18
<p>Для добавления элементов к срезу используется встроенная функция append(). Она возвращает новый срез с добавленными элементами:</p>
18
<p>Для добавления элементов к срезу используется встроенная функция append(). Она возвращает новый срез с добавленными элементами:</p>
19
<p>Если вместимость позволяет, append() просто дописывает значение. Если нет - Go выделяет новый массив с увеличенной вместимостью.</p>
19
<p>Если вместимость позволяет, append() просто дописывает значение. Если нет - Go выделяет новый массив с увеличенной вместимостью.</p>
20
<p>Можно добавить сразу несколько элементов:</p>
20
<p>Можно добавить сразу несколько элементов:</p>
21
<p>Можно объединять срезы:</p>
21
<p>Можно объединять срезы:</p>
22
<p>Это удобно, например, при объединении результатов нескольких запросов или списков.</p>
22
<p>Это удобно, например, при объединении результатов нескольких запросов или списков.</p>
23
<h2>Доступ к элементам</h2>
23
<h2>Доступ к элементам</h2>
24
<p>Срезы поддерживают доступ по индексу. В этом смысле поведение идентично массивам:</p>
24
<p>Срезы поддерживают доступ по индексу. В этом смысле поведение идентично массивам:</p>
25
<p>Если обратиться по индексу, которого нет в срезе - будет ошибка во время выполнения (runtime panic), а не на этапе компиляции.</p>
25
<p>Если обратиться по индексу, которого нет в срезе - будет ошибка во время выполнения (runtime panic), а не на этапе компиляции.</p>
26
<p>Поэтому перед обращением стоит проверять длину через len().</p>
26
<p>Поэтому перед обращением стоит проверять длину через len().</p>
27
<h2>Передача и возврат из функции</h2>
27
<h2>Передача и возврат из функции</h2>
28
<p>Когда срез передаётся в функцию, копируется только небольшой "заголовок" среза: длина, вместимость и указатель на общий подлежащий массив. Сами элементы не копируются. Поэтому если внутри функции менять элементы по индексу (s[i] = ...), изменения видны и снаружи. Это остаётся передачей по значению (копируется заголовок), просто в нём хранится указатель на данные.</p>
28
<p>Когда срез передаётся в функцию, копируется только небольшой "заголовок" среза: длина, вместимость и указатель на общий подлежащий массив. Сами элементы не копируются. Поэтому если внутри функции менять элементы по индексу (s[i] = ...), изменения видны и снаружи. Это остаётся передачей по значению (копируется заголовок), просто в нём хранится указатель на данные.</p>
29
<p>Также можно возвращать срез из функции:</p>
29
<p>Также можно возвращать срез из функции:</p>
30
<p>Подробнее о нюансах append() при работе внутри функций (и почему иногда изменения "не выходят" наружу) узнаем в одном из следующих уроков.</p>
30
<p>Подробнее о нюансах append() при работе внутри функций (и почему иногда изменения "не выходят" наружу) узнаем в одном из следующих уроков.</p>
31
<h2>Пример: заполнение среза в цикле и возврат</h2>
31
<h2>Пример: заполнение среза в цикле и возврат</h2>
32
<h2>Сравнение с массивами</h2>
32
<h2>Сравнение с массивами</h2>
33
33