Вопрос с собеседования · Go

Почему изменение одного среза неожиданно меняет другой?

JuniorСрезы, мапы и строки
Ещё спрашивают так:
  • Какие ловушки есть у append в Go?
Коротко

Срезы, полученные друг из друга, делят базовый массив, пока append не перевыделил его. Если у исходного среза есть свободная ёмкость, append к подсрезу перезапишет элементы исходного. Поведение зависит от cap — поэтому баг проявляется не всегда.

  • Защита: копия (slices.Clone, copy) или полное выражение среза a[i:j:j]
  • Возвращая внутренний срез из структуры, отдают доступ к её данным
  • Две горутины, делающие append к одному срезу, — гонка

А ещё Тот же эффект при передаче среза в функцию, которая делает append

Попробуйте сами

Срезы Go: len, cap, 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 минут бесплатно.

Разбор собеседования