copy
The previous lesson showed that slicing shares memory. copy is Go's built-in function for copying elements from one slice into another. Reach for it when you want an independent duplicate: create a destination slice first, then copy the source elements into it.
The basics
copy(dst, src) copies elements from src into dst. It stops when it runs out of source elements or destination room. The return value tells you how many elements were actually copied:
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 5)
n := copy(dst, src)
fmt.Println(dst) // [1 2 3 4 5]
fmt.Println(n) // 5
The destination has to exist already. copy uses whatever length dst already has; it does not grow it for you. If dst is shorter than src, extra source elements are silently dropped. If dst is longer, its trailing elements are left alone.
Two short snippets make that rule concrete:
a := make([]int, 3)
b := []int{1, 2, 3, 4, 5}
copy(a, b)
fmt.Println(a) // [1 2 3] (b's tail 4, 5 was dropped)
c := make([]int, 5)
d := []int{7, 8}
copy(c, d)
fmt.Println(c) // [7 8 0 0 0] (c's tail was left at zero)
copy breaks aliasing
Here is the whole point of this lesson. copy creates a genuinely independent set of elements. Writing through the destination does not touch the source, and vice versa:
src := []int{1, 2, 3}
dst := make([]int, 3)
copy(dst, src)
dst[0] = 999
fmt.Println(src) // [1 2 3] (untouched)
fmt.Println(dst) // [999 2 3]
Compare with the aliasing scenarios from the previous lesson, where a write through middle or view changed the original. Here the two slices have separate backing arrays, and they stay separate no matter what either one does afterwards.
This is the right pattern when you want to return a slice from a function without exposing your internal storage, or when you want to snapshot a slice before mutating it.
copy handles overlap correctly
copy is safe even when source and destination overlap in the same underlying array, which lets you shift elements around without writing a manual loop:
nums := []int{1, 2, 3, 4, 5}
copy(nums[0:4], nums[1:5])
nums = nums[:4]
fmt.Println(nums) // [2 3 4 5]
That copied nums[1:5] ([2, 3, 4, 5]) into nums[0:4], shifting everything one position to the left. Then nums = nums[:4] dropped the duplicated last slot. Together, those two steps delete the first element without an allocation.
The starter declares src := []int{10, 20, 30, 40, 50}. In main:
- Create
dstas a slice of five zeros usingmake. - Copy
srcintodst. - Change
dst[0]to999. - Print
srcanddston separate lines.
If src also changed, you have an aliasing bug. With copy used correctly, src should still read [10 20 30 40 50].
[10 20 30 40 50] [999 20 30 40 50]