How to copy or duplicate a slice in Go

The builtin copy() and append() functions eases the task of duplicating slices in Go, but you’ll need to watch out for some edge cases

Copying the elements of a slice to another slice is straightforward in Go, but it should be done using the builtin copy or append functions. If you simply assign an existing slice to a new variable, the slice will not be duplicated.

Both variables will refer to the exact same slice, so any changes to the slice’s value will be reflected in all its references. This is also true when passing a slice to a function since slices are passed by reference in Go.

func changeSlice(s []int) {
	if len(s) > 0 {
		s[0] = 5
	}
}

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := s1

	fmt.Println(s1, s2) // [1 2 3 4 5] [1 2 3 4 5]

	s2[1] = 10 // this change is reflected in both s1 and s2
	fmt.Println(s1, s2) // [1 10 3 4 5] [1 10 3 4 5]

	changeSlice(s1)
	fmt.Println(s1, s2) // [5 10 3 4 5] [5 10 3 4 5]
}

Using copy

To clone a slice without affecting the original, the copy function can be used as shown below:

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := make([]int, len(s1))

	copy(s2, s1)
	fmt.Println(s1, s2) // [1 2 3 4 5] [1 2 3 4 5]

	s2[1] = 10 // changing s2 does not affect s1
	fmt.Println(s1, s2) // [1 2 3 4 5] [1 10 3 4 5]
}

The copy function takes the destination slice as the first argument and the source slice as the second, and both slices must be of the same type. The return value of copy is the number of elements that was copied. If you don’t need this value, you can just ignore it as I’ve done above.

When using the copy function, there one major thing to note: the lengths of both destination and source slices do not have to be equal. It will only copy up to the smaller number of elements. So if you attempt to copy to an empty or nil slice, nothing will be copied.

func main() {
	s1 := []int{1, 2, 3, 4, 5} // s1 is length 5
	s2 := make([]int, 3)       // s2 is length 3

	copy(s2, s1)
	fmt.Println(s2) // [1 2 3]

	var s3 []int // nil slice of length 0
	copy(s3, s1)
	fmt.Println(s3) // []
}

Using append

The builtin append function may also be used to copy a slice in Go. Here’s how:

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := []int{}
	s2 = append([]int(nil), s1...)

	fmt.Println(s1, s2) // [1 2 3 4 5] [1 2 3 4 5]

	s1[0] = 20
	fmt.Println(s1, s2) // [20 2 3 4 5] [1 2 3 4 5]
}

In this case, the elements of s1 is appended to a nil slice and the resulting slice is assigned to s2. This method duplicates the entire slice regardless of the length of the destination unlike copy above. Also note that the length of the destination slice may be truncated or increased according to the length of the source.

Edge cases

There are some edge cases with both methods discussed above. With copy, the destination slice will not be nil even if the source slice is nil:

func main() {
	var s1 []int
	s2 := make([]int, len(s1))

	copy(s2, s1)
	fmt.Println(s1 == nil, s2 == nil) // true false
}

You can use the code below to fix this problem:

func main() {
	var s1 []int
	s2 := make([]int, len(s1))

	if s1 == nil {
		s2 = nil
	} else {
		copy(s2, s1)
	}

	fmt.Println(s1 == nil, s2 == nil) // true true
}

With append, the destination slice will be nil if the source slice is a non-nil slice with zero elements:

func main() {
	s1 := make([]int, 0)
	var s2 []int
	s2 = append([]int(nil), s1...)

	fmt.Println(s1 == nil, s2 == nil) // false true
}

This can be fixed by creating a three-index subslice of the original slice and append to that instead while ensuring that the result does not share any elements with the source (by restricting the length and capacity of the subslice to be zero).

func main() {
	s1 := make([]int, 0)
	var s2 []int
	s2 = append(s1[:0:0], s1...)

	fmt.Println(s1 == nil, s2 == nil) // false false
}

Wrap up

In this article, we looked at two different ways to clone a slice in Go, and also considered some edge cases associated with both methods. If you have any further contribution, feel free to leave a comment below.

Thanks for reading, and happy coding!