Golang: Копирование срезов
Срезы (slices) в Go передаются по значению, но содержат указатель на данные, поэтому простое присваивание приводит к совместному использованию одной и той же области памяти. Изменения в одном срезе могут повлиять на другой:
a := []int{1, 2, 3}
b := a
b[0] = 100
// изменился и a
fmt.Println(a) // => [100 2 3]
fmt.Println(b) // => [100 2 3]Чтобы избежать таких побочных эффектов, нужно явно копировать содержимое среза.
Копирование с помощью slices.Clone()
Стандартный пакет slices предлагает удобную функцию Clone(), которая создаёт новый срез с тем же содержимым, не изменяя оригинал:
import "slices"
original := []int{1, 2, 3}
clone := slices.Clone(original)
clone[0] = 100
fmt.Println(original) // => [1 2 3]
fmt.Println(clone) // => [100 2 3]Функция Clone всегда создаёт новый срез с собственной памятью.
Частичное копирование с помощью copy()
Если нужно скопировать данные из одного среза в другой, используйте функцию copy():
src := []int{1, 2, 3}
dst := make([]int, 2)
copy(dst, src)
fmt.Println(dst) // => [1 2]Копируется min(len(src), len(dst)) элементов.
Копирование с помощью append()
Ещё один способ безопасно скопировать срез — использовать append:
src := []int{1, 2, 3}
dst := append([]int(nil), src...) // или: append(make([]int, 0, len(src)), src...)Создаётся новый срез, и содержимое копируется в него.
Почему простого присваивания недостаточно
Присваивание среза копирует только структуру (указатель, длину и ёмкость), но не сам массив. В результате оба среза указывают на один и тот же участок памяти:
a := []string{"hello", "world"}
b := a
b[0] = "hi"
// слайс 'a' изменился!
fmt.Println(a) // => [hi world]
fmt.Println(b) // => [hi world]Пример: безопасное клонирование перед модификацией
import "slices"
func transform(data []int) []int {
clone := slices.Clone(data)
for i := range clone {
clone[i] *= 2
}
return clone
}
func main() {
values := []int{1, 2, 3}
newValues := transform(values)
fmt.Println(values) // => [1 2 3]
fmt.Println(newValues) // => [2 4 6]
}Рекомендации
- Функция
slices.Clone()удобна для создания независимой копии среза. - Используйте
copy()илиappend()для ручного клонирования или переноса элементов. - Избегайте присваивания (
b := a), если ожидается независимость данных.
Задание
Реализуйте функцию IntsCopy(src []int, maxLen int) []int, которая создает копию слайса src с длиной maxLen. Если maxLen равен нулю или отрицательный, то функция возвращает пустой слайс []int{}. Если maxLen больше длины src, то возвращается полная копия src.
Полезное
Golang: Копирование срезов
Срезы (slices) в Go передаются по значению, но содержат указатель на данные, поэтому простое присваивание приводит к совместному использованию одной и той же области памяти. Изменения в одном срезе могут повлиять на другой:
a := []int{1, 2, 3}
b := a
b[0] = 100
// изменился и a
fmt.Println(a) // => [100 2 3]
fmt.Println(b) // => [100 2 3]Чтобы избежать таких побочных эффектов, нужно явно копировать содержимое среза.
Копирование с помощью slices.Clone()
Стандартный пакет slices предлагает удобную функцию Clone(), которая создаёт новый срез с тем же содержимым, не изменяя оригинал:
import "slices"
original := []int{1, 2, 3}
clone := slices.Clone(original)
clone[0] = 100
fmt.Println(original) // => [1 2 3]
fmt.Println(clone) // => [100 2 3]Функция Clone всегда создаёт новый срез с собственной памятью.
Частичное копирование с помощью copy()
Если нужно скопировать данные из одного среза в другой, используйте функцию copy():
src := []int{1, 2, 3}
dst := make([]int, 2)
copy(dst, src)
fmt.Println(dst) // => [1 2]Копируется min(len(src), len(dst)) элементов.
Копирование с помощью append()
Ещё один способ безопасно скопировать срез — использовать append:
src := []int{1, 2, 3}
dst := append([]int(nil), src...) // или: append(make([]int, 0, len(src)), src...)Создаётся новый срез, и содержимое копируется в него.
Почему простого присваивания недостаточно
Присваивание среза копирует только структуру (указатель, длину и ёмкость), но не сам массив. В результате оба среза указывают на один и тот же участок памяти:
a := []string{"hello", "world"}
b := a
b[0] = "hi"
// слайс 'a' изменился!
fmt.Println(a) // => [hi world]
fmt.Println(b) // => [hi world]Пример: безопасное клонирование перед модификацией
import "slices"
func transform(data []int) []int {
clone := slices.Clone(data)
for i := range clone {
clone[i] *= 2
}
return clone
}
func main() {
values := []int{1, 2, 3}
newValues := transform(values)
fmt.Println(values) // => [1 2 3]
fmt.Println(newValues) // => [2 4 6]
}Рекомендации
- Функция
slices.Clone()удобна для создания независимой копии среза. - Используйте
copy()илиappend()для ручного клонирования или переноса элементов. - Избегайте присваивания (
b := a), если ожидается независимость данных.
Задание
Реализуйте функцию IntsCopy(src []int, maxLen int) []int, которая создает копию слайса src с длиной maxLen. Если maxLen равен нулю или отрицательный, то функция возвращает пустой слайс []int{}. Если maxLen больше длины src, то возвращается полная копия src.
Полезное
Ваше упражнение проверяется по этим тестам
package solution
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMap(t *testing.T) {
a := assert.New(t)
a.Equal([]int{}, IntsCopy([]int{}, 0))
a.Equal([]int{}, IntsCopy([]int{1, 2}, 0))
a.Equal([]int{}, IntsCopy([]int{1, 2}, -1))
a.Equal([]int{}, IntsCopy([]int{1, 2}, -5))
a.Equal([]int{1, 2}, IntsCopy([]int{1, 2, 3}, 2))
a.Equal([]int{1, 2, 3, 4}, IntsCopy([]int{1, 2, 3, 4}, 4))
a.Equal([]int{1, 2, 3, 4, 5}, IntsCopy([]int{1, 2, 3, 4, 5}, 10))
}Решение учителя откроется через:
20:00
