> Какие способы копирования слайса в Go существуют (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Ozon, Лига Цифровой Экономики

Стек: Go

> Пример ответа

Короткий ответ

Основные способы копирования слайса в Go: copy(dst, src) для копирования элементов, срез src[:] для создания нового слайса с общим базовым массивом, append([]T(nil), src...) для полного копирования с новым массивом, и slices.Clone(src) из стандартной библиотеки (Go 1.21+). Выбор зависит от того, нужна ли независимая копия данных или достаточно нового заголовка слайса с общим массивом.

Подробное объяснение

Слайс в Go - это структура из трёх полей: указатель на массив, длина и ёмкость. Копирование слайса может означать два разных действия:

  1. Копирование заголовка слайса - создание новой переменной, которая ссылается на тот же базовый массив. Это происходит при простом присваивании b := a или при срезе b := a[1:3]. Изменения элементов через b будут видны через a, так как массив общий.

  2. Копирование данных - создание нового массива и копирование в него элементов. Это делается через copy, append или slices.Clone.

Основные способы:

  • copy(dst, src) - стандартная функция, копирует элементы из src в dst. Копируется min(len(dst), len(src)) элементов. Важно: dst должен быть заранее выделен нужного размера, иначе часть данных потеряется.

  • append([]T(nil), src...) - идиоматический способ создать полную независимую копию. Выделяет новый массив нужной ёмкости и копирует все элементы. Работает для любого типа T.

  • slices.Clone(src) - добавлена в Go 1.21, делает то же самое, что и append, но читабельнее и оптимизирована.

  • Срез src[:] - не копирует данные, только создаёт новый заголовок. Используется, когда нужно передать слайс в функцию, не меняя его, или когда общий массив допустим.

  • append(src[:0:0], src...) - трюк для копирования с сохранением исходного слайса, но без изменения его ёмкости. Редко используется, обычно достаточно append([]T(nil), src...).

Важно помнить: все способы копирования данных выполняют shallow copy - если элементы слайса являются указателями, ссылками или структурами с указателями, сами объекты не копируются.

На практике

Выбор способа зависит от контекста:

  • Если нужно передать слайс в функцию и гарантировать, что она не изменит исходные данные - используйте slices.Clone или append.
  • Если нужно скопировать только часть элементов - используйте copy с предварительно выделенным dst.
  • Если нужен новый слайс с тем же массивом (например, для ограничения длины) - используйте срез src[:n].
  • Если копируете слайс байт в буфер - copy - самый эффективный вариант.
  • Для копирования в рамках одной функции, где исходный слайс больше не нужен, можно использовать append без выделения нового массива, если ёмкость позволяет.

Пример кода

GO
package main
import (
"fmt"
"slices"
)
func main() {
src := []int{1, 2, 3, 4, 5}
// 1. Простое присваивание - общий массив
a := src
a[0] = 100
fmt.Println(src[0]) // 100 - изменения видны
// 2. copy - нужно выделить dst заранее
dst := make([]int, len(src))
n := copy(dst, src)
fmt.Println(n, dst) // 5 [1 2 3 4 5]
// 3. append - полная копия
b := append([]int(nil), src...)
b[0] = 200
fmt.Println(src[0]) // 100 - исходный не изменился
// 4. slices.Clone (Go 1.21+)
c := slices.Clone(src)
c[1] = 300
fmt.Println(src[1]) // 2 - исходный не изменился
// 5. Срез - новый заголовок, общий массив
d := src[1:3]
d[0] = 400
fmt.Println(src[1]) // 400 - изменения видны
}

Как отвечать на собеседовании

Начните с разграничения двух понятий: копирование заголовка слайса и копирование данных. Покажите, что понимаете устройство слайса (указатель, длина, ёмкость). Затем перечислите основные способы с акцентом на copy и append, упомяните slices.Clone как современный вариант. Обязательно отметьте shallow copy и что элементы-указатели не копируются глубоко. Приведите пример, когда какой способ уместен. Если спросят про производительность - copy быстрее, так как не выделяет память, если dst уже выделен; append и slices.Clone выделяют новый массив.

Что проверяет интервьюер

Интервьюер проверяет:

  • Понимание внутреннего устройства слайса (заголовок vs массив).
  • Знание стандартной библиотеки и идиом Go.
  • Умение различать поверхностное и глубокое копирование.
  • Понимание trade-off между выделением памяти и общим массивом.
  • Способность выбрать правильный инструмент под задачу.

Типичные ошибки

  • Использование copy без предварительного выделения dst - копируется меньше элементов или ничего.
  • Утверждение, что b := a создаёт копию данных - на самом деле это копия заголовка.
  • Забывают, что copy копирует только min(len(dst), len(src)) элементов.
  • Путают slices.Clone с slices.Copy (последней не существует, есть только copy).
  • Считают, что срез src[:] копирует данные - это не так.
  • Не упоминают shallow copy для слайсов с указателями или структурами с ссылочными полями.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью