Как слайсы работают
Слайсы в Go — это динамические представления массивов, которые позволяют работать с последовательностями данных переменной длины. В отличие от массивов, размер которых фиксирован, слайсы могут изменять свой размер, что делает их более гибкими и удобными для большинства задач. Слайс — это структура, содержащая указатель на первый элемент базового массива, длину (length) и вместимость (capacity). Длина — это количество элементов в слайсе, а вместимость — максимальное количество элементов, которое может вместить слайс без перераспределения памяти.
Как устроен слайс
Слайс состоит из трёх компонентов:
- Указатель — ссылка на первый элемент слайса в базовом массиве.
- Длина (length) — количество элементов, доступных в слайсе. Длина не может превышать вместимость.
- Вместимость (capacity) — общее количество элементов в базовом массиве, начиная с первого элемента слайса. Вместимость показывает, насколько слайс может вырасти без выделения новой памяти.
Эти три значения хранятся в заголовке слайса, который передаётся по значению, но ссылается на общий массив данных.
Создание и инициализация
Слайс можно создать несколькими способами:
-
Литерал слайса:
gos := []int{1, 2, 3}При этом создаётся массив из трёх элементов, и слайс ссылается на него.
-
Функция
make:gos := make([]int, 5) // длина 5, вместимость 5Можно указать длину и вместимость отдельно:
make([]int, 3, 5)— длина 3, вместимость 5. -
Срез массива:
goarr := [5]int{1, 2, 3, 4, 5} s := arr[1:4] // включает элементы arr[1], arr[2], arr[3]Срез создаёт слайс, который ссылается на подмножество элементов массива.
Динамическое расширение с помощью append
Основная особенность слайсов — возможность динамически изменять размер с помощью встроенной функции append:
s := make([]int, 0, 5)
s = append(s, 1, 2, 3, 4, 5) // добавляет элементыЕсли при добавлении элементов вместимость слайса оказывается недостаточной, Go автоматически выделяет новый массив большего размера (обычно удваивая вместимость) и копирует в него все существующие элементы. Это операция может быть затратной, поэтому важно заранее резервировать достаточную вместимость, если ожидается много добавлений.
Работа с памятью и общие данные
Слайсы не хранят данные напрямую — они ссылаются на базовый массив. Это означает, что несколько слайсов могут указывать на одни и те же данные, и изменения через один слайс будут видны через другие. Например:
fruits := []string{"apple", "orange", "banana", "grape", "plum"}
fmt.Println(fruits[1:4]) // [orange banana grape]
fruitsSlice := fruits[2:]
fruitsSlice[0] = "strawberry" // изменяет fruits[2]
fmt.Println(fruits) // [apple orange strawberry grape plum]Это поведение важно учитывать, чтобы избежать неожиданных побочных эффектов. Если нужно создать независимую копию данных, используйте copy или срез с полной копией.
Подводные камни
- Изменение общих данных: как показано выше, слайсы, ссылающиеся на один массив, влияют друг на друга.
- Потеря элементов при
append: если слайс имеет недостаточную вместимость,appendсоздаёт новый массив, и старые слайсы, ссылающиеся на старый массив, остаются неизменными. - Срез с нулевой длиной:
s := arr[2:2]создаёт слайс с длиной 0, но вместимостью, равнойlen(arr)-2. Это может привести к неожиданному поведению приappend.
Коротко
- Слайс — это структура из указателя, длины и вместимости, ссылающаяся на базовый массив.
- Создаётся через литерал,
makeили срез массива. appendдинамически расширяет слайс, при необходимости выделяя новый массив.- Несколько слайсов могут разделять один массив, поэтому изменения через один видны другим.
- Для независимой копии используйте
copyили создавайте новый слайс с полным копированием.
