Слайсы (slice, срез) в Go это структура данных, которая позволяет хранить наборы элементов одного типа. В отличии от массивов, у которых размер задается на этапе компиляции, слайсы могут иметь любой размер.
// для того, чтобы создать слайс достаточно указать [] перед названием типа
var intSlice []int
var stringSlice []stringСтоит отметить, что слайс это структура данных, в основе которой находится массив. Как под капотом выглядит структура слайса можно посмотреть в пакете runtime
package runtime
// ...
type slice struct {
array unsafe.Pointer // указатель на массив
len int // количество элементов в слайсе
cap int // общая вместимость слайса
}

Таким образом, для того, чтобы получить размер слайса нам необходимо просто вернуть значение len из внутренней структуры данных.
Обратите внимание, что размер массива внутри слайса фиксированный, но при этом слайс имеет неограниченный размер.
Это происходит по той причине, что при переполнении вместимости (cap) мы будем создавать новый массив, в который скопируем старые значения и запишем новый.
Как работает этот механизм мы рассмотрим отдельно.
Способы создания слайса
// Можно создать slice через var декларацию
var slice []int
// под капотом это будет выглядеть так
slice{
array: nil,
len: 0,
cap: 0,
}
// Можно создать слайс через := и сразу указать значения
slice := []int{1,2,3}
// под капотом это будет выглядеть так
slice{
array: &[1,2,3],
len: 3,
cap: 3,
}Обратите внимание, что при такой инициализации слайса мы не можем влиять на вместимость слайса (cap), что важно для многих оптимизаций.
Как получить доступ к элементу слайса?
// Нумерация элементов слайса начинается с нуля
slice := []int{100,200,300}
println(arr[1]) // выведет 200Создание среза через make
В Go есть встроенная функция make, которая позволяет инициализировать слайс.
// создаем слайс с длиной 0 и вместимостью 10
slice1 := make([]int, 0, 10)
slice{
array: [],
len: 0,
cap: 10,
}
// создаем слайс с длиной 3 и вместимостью 10
slice2 := make([]int, 3, 5)
slice{
array: [0,0,0],
len: 3,
cap: 5,
}Важно, что при использовании функции make длина среза должна быть больше вместимости, иначе мы получим ошибку на этапе компиляции.
Использование функции make для создания срезов полезно, когда мы хотим оптимизировать работу с памятью. Например, мы можем сразу зарезервировать необходимое количество памяти для наших данных и не тратить время на дополнительные аллокации (выделение дополнительной памяти) в runtime.
Добавление и удаление элементов
Для добавления элементов в слайс можно использовать функцию append, которая добавит элемент в конец среза. При этом добавление элемента в слайс, где вместимость равняется длине, приведет к выделению нового участка памяти.
Проще говоря, слайс расширится сам и дополительно ничего делать не нужно.
slice := []int{1,2,3}
slice = append(arr, 7, 8)
// Важно, что append не изменяет исходный слайс, а возвращает новый
// slice => []int{1,2,3,7,8}
Обратите внимание, что мы можем передавать любое количество параметров в функцию append (от 1 до бесконечности) для добавление элементов.
Копирование срезов
// Функция копирует в dst слайс значения из src среза и возвращает количество скопированных элементов, которое будет минимальным из len(src) и len(dst).
func copy(dst, src []Type) intНа практике копирование слайса достаточно полезная функция, так как позволяет:
- Избежать непреднамеренных изменений срезов;
- Ограничить
cap, чтобы не удерживать лишнюю память; - Оптимизировать производительность;
- И многое другое.
Подробнее о сложностях и неочевидных моментах работы со срезами мы поговорим в следующем разделе.
Leave a Reply