Разделяемый базовый массив, copy и подсрезы
Содержание курса
Функция removeCopy: удаление элемента без изменения исходного среза
Допустим, есть срез []int{10, 20, 30, 40, 50} и нужно получить новый срез без элемента по индексу i, не трогая оригинал. Подсрез здесь не поможет — он разделяет базовый массив. Нужен отдельный массив длиной len(s) - 1, в который копируются два куска: до удаляемого элемента и после него.
package main
import "fmt"
func removeCopy(s []int, i int) []int {
dst := make([]int, len(s)-1)
// копируем элементы до индекса i
copy(dst, s[:i])
// копируем элементы после индекса i, начиная с позиции i в dst
copy(dst[i:], s[i+1:])
return dst
}
func main() {
original := []int{10, 20, 30, 40, 50}
result := removeCopy(original, 2) // удаляем 30
fmt.Println("original:", original) // [10 20 30 40 50]
fmt.Println("result:", result) // [10 20 40 50]
}
Первый copy(dst, s[:i]) записывает элементы s[0], s[1] в dst[0], dst[1]. Второй copy(dst[i:], s[i+1:]) стартует с dst[2] и пишет туда s[3], s[4]. В итоге dst содержит [10, 20, 40, 50].
Почему original не изменился? Потому что dst — это отдельный массив, созданный через make. Когда removeCopy получает срез s, она получает копию описания (указатель + len + cap), но читает элементы исходного массива, не пишет в него. Всё, что функция записывает, уходит в dst.
Один момент, который легко упустить: copy(dst[i:], s[i+1:]) — это тоже подсрез dst, и он указывает на тот же базовый массив dst. Это нормально: мы сдвигаем точку старта записи, чтобы не перезаписать то, что уже положил первый copy. После возврата функции оба подсреза выходят из области видимости, наружу уходит только dst.
Диапазон корректных значений i — от 0 до len(s)-1 включительно. При i == 0 первый copy копирует ноль элементов (s[:0] пуст), и это нормально — copy просто ничего не делает. При i == len(s)-1 второй copy копирует ноль элементов (s[len(s):] пуст) — та же ситуация.
