Какие способы копирования слайса в Go существуют
Что проверяют
Понимаете ли вы, что слайс это заголовок из указателя, длины и ёмкости, а не сами данные. Вопрос про копирование это повод выйти на общий массив: следом почти всегда спрашивают, что случится с исходным слайсом после append в его срез и чем поверхностная копия отличается от глубокой.
Как отвечать
Назовите рабочие способы и разницу между ними:
slices.Clone(src): идиоматичный вариант с Go 1.21, когда пакетslicesвъехал в стандартную библиотеку. Внутри этоappend(s[:0:0], s...), дляnilвернётnil;copy:dst := make([]T, len(src))и следомcopy(dst, src). Копирует ровноmin(len(dst), len(src))элементов, возвращает их число, внутри этоmemmove;append([]T(nil), src...): короче на строку, аллокация одна, ёмкость может оказаться больше исходной;- цикл
for i := range src: нужен, только если по пути данные преобразуются или фильтруются.
Отдельно проговорите, что dst := src[:] копией не является: заголовок новый, массив тот же, и запись через dst[0] видна в src.
Дальше главное: все перечисленные способы дают поверхностную копию. Для []*User, [][]int или структур с полями-слайсами скопируются указатели, и правка вложенного объекта видна обоим слайсам. Глубокую копию пишут руками, slices.Clone её не делает.
Хорошо добавить про полное выражение среза: src[:2:2] фиксирует ёмкость, и следующий append гарантированно выделит новый массив вместо затирания src[2]. Это стандартный приём, когда слайс отдают наружу из функции или из структуры.
Где спотыкаются
Считают, что присваивание слайса копирует данные. Копируется заголовок, массив остаётся общим, и функция, которая «просто читает» слайс, легко портит данные вызывающего.
Пишут dst := make([]int, 0, len(src)), следом copy(dst, src), получают пустой слайс и долго ищут ошибку. Смотреть надо на длину приёмника, ёмкость copy не интересует.
Забывают, что копия поверхностная. На собеседовании это вылезает в вопросе про слайс структур с указателями, где кандидат уверенно отвечает, что оригинал теперь защищён от изменений.