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
}
Conclusion
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!