Ответ
В Go все аргументы, включая слайсы, передаются в функцию по значению. Однако, сам слайс — это легковесная структура (заголовок), содержащая указатель на базовый массив, длину (len
) и ёмкость (cap
). При передаче слайса в функцию копируется именно эта структура, а не сам массив данных.
Из-за этого возникает два сценария:
- Изменение элементов существующего массива: Если функция изменяет элементы слайса, не выходя за пределы его
len
, изменения будут видны снаружи, так как и оригинальный, и скопированный заголовок указывают на один и тот же массив. - Изменение самого слайса (длины/ёмкости): Если функция расширяет слайс с помощью
append
, и это приводит к превышениюcap
, будет выделен новый, больший массив. Заголовок слайса внутри функции обновится и будет указывать на этот новый массив, но заголовок оригинального слайса останется без изменений.
Чтобы изменения длины/ёмкости были видны вне функции, есть два основных способа:
Способ 1: Возврат нового слайса (идиоматичный подход)
Это самый распространенный и рекомендуемый способ в Go. Функция возвращает измененный слайс, который нужно присвоить исходной переменной.
// appendToSlice возвращает новый слайс
func appendToSlice(s []int, vals ...int) []int {
return append(s, vals...)
}
func main() {
s := []int{1, 2, 3}
// Важно переприсвоить результат, так как append может вернуть новый слайс
s = appendToSlice(s, 4, 5)
fmt.Println(s) // Вывод: [1 2 3 4 5]
}
Способ 2: Использование указателя на слайс
Этот способ менее популярен, но также работает. Функция принимает указатель на слайс и изменяет его по этому указателю.
// extendSlice принимает указатель на слайс и изменяет его
func extendSlice(s *[]int, vals ...int) {
*s = append(*s, vals...)
}
func main() {
s := []int{1, 2, 3}
extendSlice(&s, 4, 5)
fmt.Println(s) // Вывод: [1 2 3 4 5]
}