Why can't I duplicate a slice with `copy()`?

GoSlice

Go Problem Overview


I need to make a copy of a slice in Go and reading the docs there is a copy function at my disposal.

> The copy built-in function copies elements from a source slice into a > destination slice. (As a special case, it also will copy bytes from a > string to a slice of bytes.) The source and destination may overlap. > Copy returns the number of elements copied, which will be the minimum > of len(src) and len(dst).

But when I do:

arr := []int{1, 2, 3}
tmp := []int{}
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

My tmp is empty as it was before (I even tried to use arr, tmp):

[]
[1 2 3]

You can check it on go playground. So why can not I copy a slice?

Go Solutions


Solution 1 - Go

The builtin copy(dst, src) copies min(len(dst), len(src)) elements.

So if your dst is empty (len(dst) == 0), nothing will be copied.

Try tmp := make([]int, len(arr)) (Go Playground):

arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

Output (as expected):

[1 2 3]
[1 2 3]

Unfortunately this is not documented in the builtin package, but it is documented in the Go Language Specification: Appending to and copying slices:

> The number of elements copied is the minimum of len(src) and len(dst).

Edit:

Finally the documentation of copy() has been updated and it now contains the fact that the minimum length of source and destination will be copied:

> Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).

Solution 2 - Go

Another simple way to do this is by using append which will allocate the slice in the process.

arr := []int{1, 2, 3}
tmp := append([]int(nil), arr...)  // Notice the ... splat
fmt.Println(tmp)
fmt.Println(arr)

Output (as expected):

[1 2 3]
[1 2 3]

As pointed out in the comments below, append may allocate excess memory if the slice isn't sized correctly to begin with. A nice solution to this is to preallocate a slice of the right capacity, like so:

tmp := append(make([]int, 0, len(arr)), arr...)

So a shorthand for copying array arr would be append(make([]int, 0, len(arr)), arr...)

https://play.golang.org/p/xwevI1chGrd

Solution 3 - Go

The copy() runs for the least length of dst and src, so you must initialize the dst to the desired length.

A := []int{1, 2, 3}
B := make([]int, 3)
copy(B, A)
C := make([]int, 2)
copy(C, A)
fmt.Println(A, B, C)

Output:

[1 2 3] [1 2 3] [1 2]

You can initialize and copy all elements in one line using append() to a nil slice.

x := append([]T{}, []...)

Example:

A := []int{1, 2, 3}
B := append([]int{}, A...)
C := append([]int{}, A[:2]...)
fmt.Println(A, B, C)    

Output:

[1 2 3] [1 2 3] [1 2]

Comparing with allocation+copy(), for greater than 1,000 elements, use append. Actually bellow 1,000 the difference may be neglected, make it a go for rule of thumb unless you have many slices.

BenchmarkCopy1-4             	50000000	        27.0 ns/op
BenchmarkCopy10-4            	30000000	        53.3 ns/op
BenchmarkCopy100-4           	10000000	       229 ns/op
BenchmarkCopy1000-4          	 1000000	      1942 ns/op
BenchmarkCopy10000-4         	  100000	     18009 ns/op
BenchmarkCopy100000-4        	   10000	    220113 ns/op
BenchmarkCopy1000000-4       	    1000	   2028157 ns/op
BenchmarkCopy10000000-4      	     100	  15323924 ns/op
BenchmarkCopy100000000-4     	       1	1200488116 ns/op
BenchmarkAppend1-4           	50000000	        34.2 ns/op
BenchmarkAppend10-4          	20000000	        60.0 ns/op
BenchmarkAppend100-4         	 5000000	       240 ns/op
BenchmarkAppend1000-4        	 1000000	      1832 ns/op
BenchmarkAppend10000-4       	  100000	     13378 ns/op
BenchmarkAppend100000-4      	   10000	    142397 ns/op
BenchmarkAppend1000000-4     	    2000	   1053891 ns/op
BenchmarkAppend10000000-4    	     200	   9500541 ns/op
BenchmarkAppend100000000-4   	      20	 176361861 ns/op

Solution 4 - Go

If your slices were of the same size, it would work:

arr := []int{1, 2, 3}
tmp := []int{0, 0, 0}
i := copy(tmp, arr)
fmt.Println(i)
fmt.Println(tmp)
fmt.Println(arr)

Would give:

3
[1 2 3]
[1 2 3]

From "Go Slices: usage and internals":

> The copy function supports copying between slices of different lengths (it will copy only up to the smaller number of elements)

The usual example is:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t

Solution 5 - Go

The best way to clone as slice is

sClone = append(s[:0:0], s...)

This implementation has two advantage:

  1. make sure that the result sClone is nil if s is nil, and is not nil if s is not nil.

  2. No need to import the containing package of type T even if T is declared in another package

Solution 6 - Go

> The Go Programming Language Specification > > 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. Both > arguments must have identical element type T and must be assignable to > a slice of type []T. The number of elements copied is the minimum of > len(src) and len(dst). As a special case, copy also accepts a > destination argument assignable to type []byte with a source argument > of a string type. This form copies the bytes from the string into the > byte slice. > > copy(dst, src []T) int > copy(dst []byte, src string) int

tmp needs enough room for arr. For example,

package main

import "fmt"

func main() {
	arr := []int{1, 2, 3}
	tmp := make([]int, len(arr))
	copy(tmp, arr)
	fmt.Println(tmp)
	fmt.Println(arr)
}

Output:

[1 2 3]
[1 2 3]

Solution 7 - Go

> NOTE: This is an incorrect solution as @benlemasurier proved

Here is a way to copy a slice. I'm a bit late, but there is a simpler, and faster answer than @Dave's. This are the instructions generated from a code like @Dave's. These is the instructions generated by mine. As you can see there are far fewer instructions. What is does is it just does append(slice), which copies the slice. This code:

package main

import "fmt"

func main() {
    var foo = []int{1, 2, 3, 4, 5}
    fmt.Println("foo:", foo)
    var bar = append(foo)
    fmt.Println("bar:", bar)
    bar = append(bar, 6)
    fmt.Println("foo after:", foo)
    fmt.Println("bar after:", bar)
}

Outputs this:

foo: [1 2 3 4 5]
bar: [1 2 3 4 5]
foo after: [1 2 3 4 5]
bar after: [1 2 3 4 5 6]

Solution 8 - Go

Sweet, Simple, Performant, No need to be careful of length, No Memory overlap, Different copies

slice2 := append([]int{}, slice1...)

Solution 9 - Go

If you don't care about the speed:

import "golang.org/x/exp/slices"

tmp := slices.Clone(arr)

With Go 1.18 and generics, any slices now could be copied with slices.Clone from package "golang.org/x/exp/slices". Playground

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSalvador DaliView Question on Stackoverflow
Solution 1 - GoiczaView Answer on Stackoverflow
Solution 2 - GoDaveView Answer on Stackoverflow
Solution 3 - GoEszeView Answer on Stackoverflow
Solution 4 - GoVonCView Answer on Stackoverflow
Solution 5 - GoDebapriya BiswasView Answer on Stackoverflow
Solution 6 - GopeterSOView Answer on Stackoverflow
Solution 7 - GoxilpexView Answer on Stackoverflow
Solution 8 - GoSumerView Answer on Stackoverflow
Solution 9 - GoshoomaView Answer on Stackoverflow