188

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?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Salvador Dali
  • 214,103
  • 147
  • 703
  • 753

11 Answers11

321

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).

icza
  • 389,944
  • 63
  • 907
  • 827
  • 7
    To summarise, `copy` doesn't contain logic for growing the destination slice if the destination slice is too small, but there is another built-in function that does: `append` While in this example it's better just to allocate the right sized slice in the first place, `append` can be used when you already have a slice and want to grow it by adding elements to the end. – thomasrutter Aug 02 '17 at 05:35
  • 3
    But *why* do I have to create a bounded size slice when copying an unbounded sized slice? – Alex Feb 20 '18 at 21:14
52

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

Dave
  • 4,356
  • 4
  • 37
  • 40
  • 19
    the catch here is that in real-world examples, which are much larger, append will allocate excess memory -- unless this array is later filled to capacity by some further processing -- because it is designed for efficient reallocation over repeated calls. https://play.golang.org/p/5_6618xnXn observe that cap(x) increases to 12, not 10. now look what happens when 1 value is added to 1048576 values https://play.golang.org/p/nz32JPehhl the capacity jumps by 2048 slots to 1050624, to accommodate only one additional value. – j. andrew shusta Dec 05 '17 at 15:02
  • 3
    Another option that allows for an in-line solution which simultaneously addresses the issue brought forth by @j.andrewshusta is to initialize the capacity of the input slice like so: `tmp := append(make([]int, 0, len(arr)), arr...)`. Under the hood, the `append` function checks the capacity of the slice on each call, allocating a new slice if the capacity is about to be exceeded. This solution has the added benefit of circumventing the repeated re-allocations. – asgaines Jan 11 '21 at 22:11
  • Just for FYI, using `copy()` from my benchmarks is about `15%` faster. OTOH `copy()` is more complicated to use, as this Go Playground illustrates https://go.dev/play/p/XDGuVU0xfRy — Benchmarks and results here https://gist.github.com/mikeschinkel/fbeb291b90970581b4428fbc14e4ab19 as you can can't benchmark reliably on a shared server. HOWEVER, I would **normally** still use the `append()` method because it is easier to understand and less likely to break than the `copy()` version unless performance is absolutely critical for a given use-case. – MikeSchinkel Feb 03 '22 at 23:03
13

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
Esze
  • 314
  • 4
  • 9
  • 4
    append should be used in cases where the array will be increased by repeated calls, as it will optimistically allocate excess capacity in anticipation of this. copy should be used once per input array in cases where the result array should be created to exact size, and is not reallocated from again. https://play.golang.org/p/0kviwKmGzx you didn't share the benchmark code that produced those results so i can't confirm or deny its validity, but it overlooks this more important aspect. – j. andrew shusta Dec 05 '17 at 15:23
  • 2
    You mean **'slice'** not _array_. They're different things. – Inanc Gumus May 11 '18 at 11:30
12

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
icza
  • 389,944
  • 63
  • 907
  • 827
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
12

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

Debapriya Biswas
  • 1,079
  • 11
  • 23
3

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

shooma
  • 435
  • 6
  • 8
2

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]
peterSO
  • 158,998
  • 31
  • 281
  • 276
1

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

slice2 := append([]int{}, slice1...)
Sumer
  • 2,687
  • 24
  • 24
0

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]
xilpex
  • 3,097
  • 2
  • 14
  • 45
  • 1
    This is incorrect, as show here: https://play.golang.org/p/q3CoEoaid6d. The expected output should reflect that of @Dave's answer: https://play.golang.org/p/mgdJ4voSlpd – ben lemasurier Aug 06 '20 at 23:06
  • 1
    @benlemasurier -- Huh... Seems you're right! Thanks for letting me know! – xilpex Aug 06 '20 at 23:08
0

Just do benchmark for those three methods which implement slice copy

  • with append on CloneWithAppend
  • with copy on CloneWithCopy
  • with append for generic any on CloneWithAny
func CloneWithAppend(b []byte) []byte {
    if b == nil {
        return nil
    }
    return append([]byte{}, b...)
}

func CloneWithCopy(b []byte) []byte {
    if b == nil {
        return nil
    }
    tmp := make([]byte, len(b))
    copy(tmp, b)
    return tmp
}

func CloneWithAny[B ~[]T, T any](b B) B {
    if b == nil {
        return nil
    }
    return append([]T{}, b...)
}

Benchmark codes

var testSlice = []byte("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM098765432112345678901234567890")

func BenchmarkCloneWithAppend(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CloneWithAppend(testSlice)
    }
}

func BenchmarkCloneWithCopy(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CloneWithCopy(testSlice)
    }
}

func BenchmarkCloneWithAny(b *testing.B) {
    for i := 0; i < b.N; i++ {
        CloneWithAny(testSlice)
    }
}

Results

goarch: amd64
pkg: test
cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
BenchmarkCloneWithAppend-12     28700232                41.50 ns/op
BenchmarkCloneWithCopy-12       32453222                30.98 ns/op
BenchmarkCloneWithAny-12        31737926                41.68 ns/op

It seems the with copy method has better performance.


Note, func Clone([]uint8) []uint8 of pkg bytes would be added in Golang next release per this commit and related proposal bytes, strings: add Clone

// Clone returns a copy of b[:len(b)].
// The result may have additional unused capacity.
// Clone(nil) returns nil.
func Clone(b []byte) []byte {
    if b == nil {
        return nil
    }
    return append([]byte{}, b...)
}
zangw
  • 43,869
  • 19
  • 177
  • 214
0

Thanks to the answerers, if someone needs to copy many lists, this generic method may helps (As said in the documentation, Go introduced native support for generics starting from version 1.18) :

func CopyList[T any](list []T) []T {
    newList := make([]T, len(list))
    copy(newList, list)
    return newList
}

usage : copiedList := CopyList(originalList)

Misagh
  • 3,403
  • 1
  • 20
  • 17