Appending to and copying slices
…
The function
copy
copies slice elements from a sourcesrc
to a destinationdst
and 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) int
Examples:
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)