Почему copy() не копирует срез в Go? Или я что-то делаю не так?
Почему copy() не заполняет пустой срез?
Вы столкнулись с классической ловушкой новичков в Go. Проблема в том, что copy() копирует минимальное количество элементов из двух срезов — min(len(dst), len(src)). Если целевой срез (dst) имеет нулевую длину, то будет скопировано 0 элементов, независимо от размера исходного среза.
В первом примере вы объявили:
var copySlice []int
Это создаёт nil-срез (или пустой срез с длиной 0). Когда вы вызываете copy(copySlice, original), Go смотрит: len(copySlice) = 0, len(original) = 5. Минимальное значение — 0, поэтому копируется 0 элементов. Никакого автоматического расширения не происходит.
Правильные способы копирования среза
Есть два основных подхода:
- С помощью make() + copy() — вы уже нашли этот способ. Сначала создаётся целевой срез нужной длины, затем копируются элементы:
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// Способ 1: создать срез той же длины и скопировать
copySlice := make([]int, len(original))
n := copy(copySlice, original)
fmt.Println("Скопировано элементов:", n) // 5
fmt.Println("copySlice:", copySlice) // [1 2 3 4 5]
// Теперь изменения копии не влияют на оригинал
copySlice[0] = 100
fmt.Println("original:", original) // [1 2 3 4 5] — не изменился
fmt.Println("copy:", copySlice) // [100 2 3 4 5]
}
- С помощью append() — более идиоматичный способ для Go, особенно если вам нужно скопировать срез в nil-срез:
// Способ 2: append к nil-срезу
var copySlice2 []int
copySlice2 = append(copySlice2, original...)
fmt.Println("copySlice2:", copySlice2) // [1 2 3 4 5]
Оператор ... "разворачивает" исходный срез, и append() сам выделяет память под все элементы.
Почему нельзя просто сделать newSlice := original?
Это очень важный момент. Присваивание newSlice := original не создаёт копию данных. Оно создаёт новую структуру среза (указатель на массив, длину, ёмкость), но указывает на тот же самый underlying array (базовый массив):
original := []int{1, 2, 3}
newSlice := original
newSlice[0] = 999
fmt.Println(original[0]) // 999 — изменилось и в оригинале!
Это поведение часто называют "shallow copy" (поверхностное копирование). Для полной независимости данных нужно использовать copy() или append().
Дополнительные советы
copy()возвращает количество скопированных элементов — используйте это для проверки.- Если нужно скопировать только часть среза, можно создать целевой срез меньшей длины:
copy(dst, src[2:5]). - Для копирования вложенных структур (срезов срезов)
copy()делает только поверхностное копирование — для глубокого копирования нужны дополнительные действия.
Надеюсь, теперь стало понятно! Ключевой принцип: copy() не выделяет память, она только копирует уже существующие элементы между двумя срезами. Выделение памяти — ваша ответственность.
<<Альтернативный взгляд: используйте copy() с избыточной ёмкостью
Первый ответ отлично объясняет проблему. Я добавлю ещё один полезный приём: можно создать целевой срез с нулевой длиной, но с ёмкостью, равной исходному срезу. Затем использовать copy на под-срезе:
original := []int{1, 2, 3, 4, 5}
copySlice := make([]int, 0, len(original)) // длина 0, ёмкость 5
copy(copySlice[:len(original)], original) // важно: используем полный срез!
fmt.Println(copySlice) // [1 2 3 4 5]
Этот подход полезен, когда вы хотите сначала выделить память, а потом заполнять её постепенно. Но для простого копирования проще использовать make([]int, len(src)) или append([]int(nil), src...).
Ещё один нюанс: если вам нужно скопировать срез и при этом изменить его длину в будущем, лучше сразу создать срез с запасом по ёмкости:
copySlice := make([]int, len(original), len(original)*2)
copy(copySlice, original)
// Теперь можно делать append без перераспределения памяти
В любом случае, запомните главное правило: copy() не изменяет длину целевого среза. Это сделано для производительности — функция не выделяет память, а только копирует байты. Если вам нужно автоматическое расширение — используйте append().