Что произойдет со слайсом, переданным в функцию, если его изменить внутри этой функции?

Ответ

Поведение зависит от типа изменения.

Слайс в Go — это легковесная структура (дескриптор), содержащая три поля:

  1. Указатель на первый элемент базового массива.
  2. Длина (len) — количество элементов в слайсе.
  3. Ёмкость (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]