ПрограммированиеGo разработчик

Как работает copy() в Go при копировании срезов? В чем особенности, ограничения и неожиданные эффекты, связанных с ростом длины и ёмкости среза? Что происходит при наложении срезов друг на друга и попытке скопировать их с перекрытием?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Функция copy(dst, src []T) int копирует элементы из src в dst.

  • Возвращает количество скопированных элементов: min(len(dst), len(src)).
  • Копирование идёт индексно: элементы src[i] копируются в dst[i].
  • Копируются содержимое (значения), не указатели на объекты.

Тонкости и ограничения:

  • Если срезы перекрываются (например, один — подмассив другого), копирование идёт так, как если бы сначала взят сниппет исходных значений, а затем скопирован (нет гарантий корректности при наложении).
  • Если dst имеет меньшую длину, чем src, скопируется только столько данных, сколько позволяет длина dst.
  • Если у dst ёмкость больше длины, расширяет ли copy срез? — Нет, только len(dst) считается целевым. Для расширения — предварительно используйте append.

Пример:

a := []int{1,2,3,4,5} b := make([]int, 3) copy(b, a) // b: [1 2 3]

Перекрытие:

x := []int{1,2,3,4} copy(x[1:], x[:3]) // [1 1 2 3]

Вопрос с подвохом

Может ли copy() использоваться для увеличения длины среза? Что произойдет, если передать в copy срез-назначение большей емкости, но меньшей длины, чем необходимо?

Ответ:

  • copy() не изменяет длину целевого среза — только копирует до len(dst)
  • Если у dst есть емкость, большую длины, сначала расширьте его через dst = dst[:newLen], затем используйте copy()

Пример, часто неочевидный:

a := []int{1,2,3} b := make([]int, 0, 5) copy(b, a) // b останется пустым, т.к. len(b)==0 b = b[:len(a)] copy(b, a) // теперь b: [1,2,3]

Примеры реальных ошибок из-за незнания тонкостей темы


История

В проекте копировали данные из одного среза в другой, рассчитывая, что copy автоматически расширит длину dst до необходимой. Это не произошло, элементы не скопировались, в результате в ответе API приходили нулевые данные. Ошибку нашли только после сравнения длины срезов — проблема оказалась в том, что у dst была большая ёмкость, но длина равна 0.


История

Часть микросервиса работала с перекрывающимися срезами, ошибочно рассчитывая, что copy всегда сработает корректно. В результате копирование вперёд разрушало оригинальные данные, возникали "невидимые" баги при работе с буферами. Решилось использованием временного буфера (copy(tmp, src), потом copy(dst, tmp)).


История

Инженер оптимизировал массив, используя copy для упорядочивания данных между срезами. Ожидал, что copy скорректирует длину dst. Оказалось, что это не происходит, и стали появляться паника и выход за рамки значимых данных — забыли корректно изменить длину среза перед копированием.