Ответ
Поведение зависит от типа изменения.
Слайс в Go — это легковесная структура (дескриптор), содержащая три поля:
- Указатель на первый элемент базового массива.
- Длина (
len
) — количество элементов в слайсе. - Ёмкость (
cap
) — максимальное количество элементов, которое может поместиться в базовый массив, начиная с указателя.
При передаче слайса в функцию копируется именно эта структура, но не сам базовый массив. Указатель в копии продолжает ссылаться на тот же массив.
Отсюда два сценария:
1. Изменение существующих элементов слайса
Если вы изменяете элементы в пределах его длины (s[i] = newValue
), эти изменения будут видны в вызывающей функции, так как вы меняете данные в общем базовом массиве.
2. Добавление элементов с помощью append
Операция append
может изменить длину и ёмкость слайса. Поскольку функция работает с копией дескриптора, эти изменения не будут видны снаружи.
- Если
cap
достаточен:append
добавит элемент в базовый массив и изменитlen
в локальной копии слайса. Вызывающая функция "не узнает" об увеличении длины. - Если
cap
недостаточен:append
создаст новый, больший массив, скопирует туда старые элементы, добавит новый и вернет слайс, указывающий уже на этот новый массив. Оригинальный слайс останется нетронутым.
Правильный подход — возвращать измененный слайс из функции.
// Эта функция НЕ изменит длину исходного слайса
func modifySliceIncorrectly(s []int) {
s[0] = 100 // Это изменение будет видно
s = append(s, 4) // Это изменение НЕ будет видно снаружи
}
// Правильный способ изменить слайс
func modifySliceCorrectly(s []int) []int {
s[0] = 200
s = append(s, 5)
return s // Возвращаем новый дескриптор слайса
}
func main() {
slice := []int{1, 2, 3}
fmt.Printf("Исходный: len=%d, cap=%d, %vn", len(slice), cap(slice), slice)
modifySliceIncorrectly(slice)
// Изменился только первый элемент. Append не сработал для вызывающей стороны.
fmt.Printf("После некорректной: len=%d, cap=%d, %vn", len(slice), cap(slice), slice)
slice = modifySliceCorrectly(slice)
// Все изменения применились, так как мы переприсвоили возвращенный слайс
fmt.Printf("После корректной: len=%d, cap=%d, %vn", len(slice), cap(slice), slice)
}
// Вывод:
// Исходный: len=3, cap=3, [1 2 3]
// После некорректной: len=3, cap=3, [100 2 3]
// После корректной: len=4, cap=6, [200 2 3 5]