Срезы в Go — это лёгкие структуры, содержащие указатель на базовый массив, длину (len
) и вместимость (cap
). При передаче в функцию срез копируется как структура, но оба среза продолжают указывать на один и тот же массив. Это поведение важно для понимания того, когда изменения внутри функции отражаются на исходных данных, а когда нет.
Передача срезов
При передаче среза в функцию изменения элементов видны снаружи, потому что копируется только структура, а не массив.
func modify(s []int) {
s[0] = 99
}
func main() {
nums := []int{1, 2, 3}
modify(nums)
fmt.Println(nums) // => [99 2 3]
}
Здесь функция modify()
изменила первый элемент, и изменение видно в main()
.
Вызов append() внутри функции
При использовании append()
поведение зависит от вместимости исходного среза:
- Если
append()
помещается в существующий массив, изменения видны снаружи. - Если
append()
создаёт новый массив (переполненаcap
), изменения не будут видны, если не вернуть новый срез.
func addElement(s []int) {
s = append(s, 100)
}
func main() {
nums := []int{1, 2, 3}
addElement(nums)
// изменений нет
fmt.Println(nums) // => [1 2 3]
}
Чтобы изменения сохранились, возвращаем срез из функции:
func addElement(s []int) []int {
return append(s, 100)
}
func main() {
nums := []int{1, 2, 3}
nums = addElement(nums)
fmt.Println(nums) // => [1 2 3 100]
}
Возврат срезов
Возвращение срезов из функций безопасно: копируется только структура, а массив остаётся в памяти столько, сколько нужен.
func newSlice() []int {
s := []int{1, 2, 3}
return s
}
func main() {
data := newSlice()
fmt.Println(data) // => [1 2 3]
}
Под-срезы и утечки памяти
Если взять под-срез большого массива, он продолжит удерживать в памяти весь массив, даже если используется только часть данных.
data := make([]int, 10000)
part := data[:10] // удерживает весь массив из 10000 элементов
Чтобы освободить неиспользуемую память, можно сделать копию:
import "slices"
data := make([]int, 10000)
part := slices.Clone(data[:10])
Вывод
- Срезы передаются по значению, но содержат указатель на общий массив.
- Изменения элементов внутри функции видны снаружи.
- При использовании
append()
новый срез может указывать на другой массив — его нужно возвращать. - Возврат срезов безопасен, но под-срезы больших массивов могут удерживать память, которую стоит освобождать через копирование.
- Чтобы избежать утечки памяти, скопируйте нужную часть среза, например через
slices.Clone()
.