Copying slices

September 3, 2024

Appending to and copying slices

The function copy copies slice elements from a source src to a destination dst 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 of len(src) and len(dst). As a special case, if the destination’s core type is []byte, copy also accepts a source argument with core type bytestring. 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)


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Making slices, maps and channels

Making slices, maps and channels The built-in function make takes a type T, optionally followed by a type-specific list of expressions. The core type of T must be a slice, map or channel. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values. Call Core type Result make(T, n) slice slice of type T with length n and capacity n make(T, n, m) slice slice of type T with length n and capacity m make(T) map map of type T make(T, n) map map of type T with initial space for approximately n elements make(T) channel unbuffered channel of type T make(T, n) channel buffered channel of type T, buffer size n The only time you absolutely need to use make is when creating channels.


Constant lengths and expressions

A few of the built-in functions are very special, in that they can evaluate to constant expressions. len and cap are two such functions. But they aren’t always evaluated to constant expressions, sometimes they’re more normal-ish runtime functions. Length and capacity … The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls; in this case s is not evaluated.


Capacity of slices

Length and capacity … The capacity of a slice is the number of elements for which there is space allocated in the underlying array. At any time the following relationship holds: 0 <= len(s) <= cap(s) Recall that a slice is backed by a fixed-length array, which may have more elements than the slice. When this is the case, the capacity of the slice is said to be the current length of the slice, plus any unused elements in the backing array that extend beyond the final element of the slice.

Get daily content like this in your inbox!

Subscribe