Appending to and copying slices
…
The function
copycopies slice elements from a sourcesrcto a destinationdstand returns the number of elements copied. The core types of both arguments must be slices with identical element type. The number of elements copied is the minimum oflen(src)andlen(dst). As a special case, if the destination’s core type is[]byte, copy also accepts a source argument with core typebytestring. This form copies the bytes from the byte slice or string into the byte slice.copy(dst, src []T) int copy(dst []byte, src string) intExamples:
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7} var s = make([]int, 6) var b = make([]byte, 5) n1 := copy(s, a[0:]) // n1 == 6, s is []int{0, 1, 2, 3, 4, 5} n2 := copy(s, s[2:]) // n2 == 4, s is []int{2, 3, 4, 5, 4, 5} n3 := copy(b, "Hello, World!") // n3 == 5, b is []byte("Hello")
There’s not a whole lot to elaborate on here. copy is pretty straight forward, and I think the spec explains it pretty well.
I suppose one topic worth addressing, though, would be: When should you use copy?
As discussed yesterday, there are times when sharing a slice with different parts of code can lead to surprising results. This would be one example of a time that a copy makes good sense.
More generally, just remember that any time you pass a slice to a function, or return one from a function, the recipient may do things to that slice that affect the backing array. If you rely on that backing array not being mutated, you probably want to do a copy.
Here’s an example I see frequently when working with constants, where a copy might make sense:
package animals
type Animal string
const (
AnimalDog = "dog"
AnimalCat = "cat"
AnimalBird = "bird"
)
var AllAnimals = []Animal{AnimalDog, AnimalCat, AnimalBird}
There’s room for some sublte abuse in this simple code. Anyone who imports this code could modify AllAnimals.
import "animals"
func init() {
animals.AllAnimals = append(animals.AllAnimals, "Dinosaur")
}
What’s the solution? Rather than exporting the slice variable directly, create an accessor function:
var allAnimals = []Animal{AnimalDog, AnimalCat, AnimalBird}
func() AllAnimals() []Animal {
return allAnimal
}
Although this doesn’t quite solve the problem, either:
list := AllAnimals() // list = [dog, cat, bird]
list[1] = "cow" // list = [dog, cow, bird], but allAnimals also = [dog, cow, bird]
The solution is to return a copy of the authoritative slice:
func() AllAnimals() []Animal {
c := make([]Animal, len(allAnimals))
copy(c, allAnimals)
return c
}
Or a shorter alternative, if you’re using Go 1.21 or newer:
func() AllAnimals() []Animal {
return slices.Clone(allAnimals)
}
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)