Почему изменение одного среза неожиданно меняет другой?
- Какие ловушки есть у append в Go?
Срезы, полученные друг из друга, делят базовый массив, пока append не перевыделил его. Если у исходного среза есть свободная ёмкость, append к подсрезу перезапишет элементы исходного. Поведение зависит от cap — поэтому баг проявляется не всегда.
- Защита: копия (slices.Clone, copy) или полное выражение среза a[i:j:j]
- Возвращая внутренний срез из структуры, отдают доступ к её данным
- Две горутины, делающие append к одному срезу, — гонка
А ещё Тот же эффект при передаче среза в функцию, которая делает append
Подробно
Механика. b := a[:2] — новый дескриптор с длиной 2, но ёмкость наследуется (10). append(b, 99) видит свободное место и пишет в третью ячейку общего массива — ту же, что a[2].
Где встречается:
- функция принимает срез, делает append и использует результат, а вызывающий продолжает работать со старым — данные перемешиваются;
- несколько срезов строятся из общего буфера в цикле;
- метод возвращает внутренний срез структуры, и снаружи его дополняют.
Правила: если срез нужно хранить или модифицировать независимо — скопировать; при нарезке общего буфера — ограничивать ёмкость третьим индексом.
Пример
a := make([]int, 3, 10) // [0 0 0], cap 10
b := a[:2] // [0 0], тот же массив
b = append(b, 99) // места хватает — пишет в a[2]
fmt.Println(a) // [0 0 99]
c := a[:2:2] // cap ограничен 2
c = append(c, 7) // новый массив, a не меняетсяЧто хотят услышать
- Общий массив и роль ёмкости.
- Способы защиты.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗append всегда создаёт новый массив
- ✓Срезы делят базовый массив, и append в подсрез затирает элементы исходного
- ✗Подсрезы получают собственную копию данных
- ✗Это ошибка компилятора, исправленная в Go 1.22
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.