Ссылочная природа срезов и копирование

1. Срезы — ссылочный тип

Поверхностное копирование при присваивании

original := []int{1, 2, 3}
copySlice := original  // Копируется только структура (ptr, len, cap)

original[0] = 999

fmt.Println(original)   // [999 2 3]
fmt.Println(copySlice)  // [999 2 3] — тоже изменился!

Механизм:

original  ──┐
            ├──→ [999, 2, 3]  (один массив в памяти)
copySlice ──┘

Оба среза указывают на один и тот же базовый массив.


2. Независимое копирование: функция copy()

Синтаксис

copy(dst, src)  // dst — куда, src — откуда

Возвращает: количество скопированных элементов.

Правило копирования

Копируется min(len(dst), len(src)) элементов

Пример: полная копия

source := []int{10, 20, 30, 40, 50}
dest := make([]int, len(source))  // Создаём приёмник той же длины

copied := copy(dest, source)

fmt.Println(source)  // [10 20 30 40 50]
fmt.Println(dest)    // [10 20 30 40 50]
fmt.Println(copied)  // 5

Проверка независимости

source[0] = 999

fmt.Println(source)  // [999 20 30 40 50]
fmt.Println(dest)    // [10 20 30 40 50]  — не изменился!

Вывод: copy() создаёт глубокую копию данных.


3. Особенности copy()

Если len(dest) < len(src)

source := []int{1, 2, 3, 4, 5}
dest := make([]int, 3)  // Только 3 элемента

copied := copy(dest, source)

fmt.Println(dest)    // [1 2 3] — скопированы первые 3
fmt.Println(copied)  // 3

Правило: Копируется столько, сколько поместится в dest.


Если len(dest) > len(src)

source := []int{1, 2, 3}
dest := make([]int, 5)  // 5 элементов, инициализированы нулями

copied := copy(dest, source)

fmt.Println(dest)    // [1 2 3 0 0]
fmt.Println(copied)  // 3

Остальные элементы сохраняют исходные значения (нули).


4. Сравнение: присваивание vs copy()

Операция Результат Независимость
s2 := s1 Копируется структура (ptr) ❌ Изменения видны через оба среза
copy(s2, s1) Копируются данные ✅ Полная независимость

5. Итерация по срезам

Способ 1: Классический for

items := []string{"apple", "banana", "cherry"}

for i := 0; i < len(items); i++ {
    fmt.Printf("[%d] = %s\n", i, items[i])
}

Вывод:

[0] = apple
[1] = banana
[2] = cherry

Способ 2: for range (индекс + значение)

for index, value := range items {
    fmt.Printf("[%d] = %s\n", index, value)
}

Тот же результат, но компактнее.


Способ 3: Только значения

for _, value := range items {
    fmt.Printf("%s\n", value)
}

Вывод:

apple
banana
cherry

_ игнорирует индекс.


Способ 4: Только индексы

for index := range items {
    fmt.Printf("Индекс: %d\n", index)
}

Вывод:

Индекс: 0
Индекс: 1
Индекс: 2

6. Практические примеры copy()

Удаление элемента (безопасно)

s := []int{1, 2, 3, 4, 5}
idx := 2  // Удаляем элемент с индексом 2

// Создаём новый срез без элемента
result := make([]int, 0, len(s)-1)
result = append(result, s[:idx]...)
result = append(result, s[idx+1:]...)

fmt.Println(result)  // [1 2 4 5]
fmt.Println(s)       // [1 2 3 4 5] — оригинал не изменён

Клонирование среза

original := []int{1, 2, 3}

// Способ 1: через copy
clone1 := make([]int, len(original))
copy(clone1, original)

// Способ 2: через append
clone2 := append([]int{}, original...)

fmt.Println(clone1)  // [1 2 3]
fmt.Println(clone2)  // [1 2 3]

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

❌ Забыли создать приёмник

var dest []int  // nil-срез, len=0
source := []int{1, 2, 3}

copied := copy(dest, source)
fmt.Println(copied)  // 0 — ничего не скопировано!

Решение: Инициализировать dest с нужной длиной:

dest := make([]int, len(source))

❌ Путаница присваивания и копирования

s1 := []int{1, 2, 3}
s2 := s1  // ❌ НЕ копия, а ссылка!

s2[0] = 999
fmt.Println(s1)  // [999 2 3] — изменился!

Решение: Использовать copy():

s2 := make([]int, len(s1))
copy(s2, s1)

8. Итоги

✅ Срезы — ссылочный тип: присваивание копирует только структуру ✅ copy(dst, src) — глубокое копирование данных ✅ Копируется min(len(dst), len(src)) элементов ✅ Функция возвращает количество скопированных элементов
✅ Перебор: классический for или for rangefor _, v := range — игнорирование индекса
for i := range — только индексы

Ключевое правило:

slice2 := slice1      → ссылка (shallow copy)
copy(slice2, slice1)  → независимая копия (deep copy)