Reputation: 222979
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?
Upvotes: 205
Views: 194031
Reputation: 3623
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)
Upvotes: 2
Reputation: 48566
Just do benchmark for those three methods which implement slice copy
append
on CloneWithAppend
copy
on CloneWithCopy
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...)
}
Upvotes: 0
Reputation: 495
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
Upvotes: 5
Reputation: 2867
Sweet, Simple, Performant, No need to be careful of length, No Memory overlap, Different copies
slice2 := append([]int{}, slice1...)
Upvotes: 2
Reputation: 1359
The best way to clone as slice is
sClone = append(s[:0:0], s...)
This implementation has two advantage:
make sure that the result sClone is nil if s is nil, and is not nil if s is not nil.
No need to import the containing package of type T even if T is declared in another package
Upvotes: 23
Reputation: 4694
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
Upvotes: 57
Reputation: 3237
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]
Upvotes: 0
Reputation: 325
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
Upvotes: 14
Reputation: 418695
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)
andlen(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).
Upvotes: 351
Reputation: 1329892
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
Upvotes: 13
Reputation: 166925
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]
Upvotes: 2