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

Опишите особенности копирования срезов и структуры в Go. Как избежать неожиданных эффектов при работе с ними?

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

Ответ

Структуры в Go копируются по значению (deep copy первого уровня, вложенные структуры копируются по тому же правилу), а срезы — по значению, но копируется лишь структура, а не подлежащий массив данных: и оригинал, и копия среза указывают на один и тот же backing array, поэтому изменение через один срез отразится в другом, если не делать явную копию массива через copy().

Пример копирования среза:

s1 := []int{1,2,3} s2 := s1 // указатели на один буфер! s2[0] = 99 fmt.Println(s1) // [99 2 3] // Для копии массива: s3 := make([]int, len(s1)) copy(s3, s1) s3[0] = 42 fmt.Println(s1) // [99 2 3], s3 — независим

При копировании структур: если структура содержит поле-указатель, поле-срез, то копируются только ссылки, а не значения.

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

Какой способ "клонировать" срез так, чтобы его изменения не затрагивали оригинал?

Потенциально ошибочный ответ: Просто сделать b := a. Верно:

  • Нужно использовать copy, создать новый срез нужной длины и скопировать данные:
newS := make([]int, len(a)) copy(newS, a)

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


История

Описание: При генерации запросов к БД динамически меняли поля среза в структуре-запросе, не делая копии. При повторных запросах данные пересекались, выдавая некорректные результаты для пользователей.


История

Описание: При параллельной обработке данных создавали копию структуры через присваивание и расширяли срез в одной горутине, параллельно используя другой оригинальный срез. Данные "перетирались", приводя к непредсказуемым багам.


История

Описание: Использовали структуру-буфер, одновременное копирование через присваивание не учитывало вложенные срезы (shallow copy), изменения в одном месте затрагивали другой буфер — в итоге было сложно отследить источник изменения данных.