Ответ
Слайсы в Go передаются в функции по значению.
Это означает, что копируется сама структура слайса (так называемый slice header), которая содержит:
- Указатель на базовый массив (underlying array).
- Длину (
len
). - Ёмкость (
cap
).
Из-за этого поведение при изменении слайса внутри функции может быть неочевидным.
Сценарий 1: Изменение существующих элементов
Так как и оригинальный слайс, и его копия в функции указывают на один и тот же базовый массив, изменение элементов через индекс будет видно снаружи.
Сценарий 2: Использование append
Здесь поведение зависит от ёмкости (cap
) слайса:
- Если
cap
достаточна:append
добавляет элемент в тот же базовый массив. Длина (len
) в копии слайса внутри функции увеличивается, ноlen
оригинального слайса не меняется. Изменения не будут видны снаружи. - Если
cap
недостаточна:append
создает новый, больший массив, копирует в него старые элементы и добавляет новый. Указатель в копии слайса теперь указывает на этот новый массив. Оригинальный слайс и его базовый массив остаются нетронутыми.
Пример проблемы:
func modifySlice(s []int) {
s[0] = 100 // Это изменение будет видно снаружи
s = append(s, 4) // Это изменение НЕ будет видно снаружи
fmt.Printf("Внутри функции: len=%d, cap=%d, data=%vn", len(s), cap(s), s)
}
func main() {
s := make([]int, 3, 3) // len=3, cap=3
s[0], s[1], s[2] = 1, 2, 3
fmt.Printf("Снаружи до: len=%d, cap=%d, data=%vn", len(s), cap(s), s)
modifySlice(s)
fmt.Printf("Снаружи после: len=%d, cap=%d, data=%vn", len(s), cap(s), s)
}
// Вывод:
// Снаружи до: len=3, cap=3, data=[1 2 3]
// Внутри функции: len=4, cap=6, data=[100 2 3 4] (создан новый массив)
// Снаружи после: len=3, cap=3, data=[100 2 3]
Правильный (идиоматичный) подход
Чтобы надежно изменять слайс, функция должна возвращать его обновленную версию.
func addElement(s []int, value int) []int {
return append(s, value)
}
func main() {
s := []int{1, 2, 3}
s = addElement(s, 4) // Переприсваиваем результат
fmt.Println(s) // Вывод: [1 2 3 4]
}
Альтернативный, но менее распространенный способ — передавать в функцию указатель на слайс (*[]int
).