I have a question about the below code
package main
import "fmt"
func main() {
var a []int
printSlice("a", a)
// append works on nil slices.
a = append(a, 0)
printSlice("a", a)
// the slice grows as needed.
a = append(a, 1)
printSlice("a", a)
// we can add more than one element at a time.
a = append(a, 2, 3, 4)
printSlice("a", a)
}
func printSlice(s string, x []int) {
fmt.Printf("%s len=%d cap=%d %v\n",
s, len(x), cap(x), x)
}
I always guess what the result of running a piece of code will look like then run the code and check if my guess is correct. But this code resulted a little bit different from my guess:
Result:
On my local go tour server:
a len=0 cap=0 []
a len=1 cap=1 [0]
a len=2 cap=2 [0 1]
a len=5 cap=6 [0 1 2 3 4]
Everything is ok until the last line but I don't get
cap=6
why not
cap=5
My opinion is I did not create slice with explicit capacity therefore my system gave it this value of 6.
2) But when I tried this same code on the golang tour server I get a little more diffferent result like this :
a len=0 cap=0 []
a len=1 cap=2 [0]
a len=2 cap=2 [0 1]
a len=5 cap=8 [0 1 2 3 4]
What about cap=2 on the second line and cap=8 on the last line?
This question isn't an exact duplicate, but my answer here also effectively answers this.
TL;DR — how much the capacity of the slice is expanded by isn't mentioned in the specification and different versions of Go (or different implementations, or the same version on different architectures, etc.) can expand the slice by different amounts.
The reason you might want to make the capacity larger than you need is because underneath the slice there is an array which is immutable (it can't be expanded). When you "grow" a slice what actually happens is that you make a new (longer) array, copy all the values over, and then set that as the backing array for the slice. If you were appending lots of values, you'd have to do lots and lots of copies (one for every single value), which would be very slow, so instead the runtime allocates more space than it thinks you need so that it has to make copies less often.
In fact, after the cap capacity is calculated, a roundupsize() function will be run, which will calculate the real cap due to memory alignment considerations.
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.cap < 1024 {
newcap = doublecap
} else {
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
if newcap <= 0 {
newcap = cap
}
}
}
......
capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
newcap = int(capmem / sys.PtrSize)
In this example, the size after the first expansion is expected to be sizeof(int64)*5=40, and then the first value greater than or equal to this number will be found in the array below, here is 48, so the true cap is 48 /sizeof(int64)=6
class_to_size = uint16{0, 8, 16, 24, 32, 48, 64, 80, 96,112, 128, 144, 160, 176, 192, 208, 224,...
As for why the results of the two runs are different, I guess it should be related to the operating system. For more details, see https://github.com/golang/go/blob/master/src/runtime/slice.go#L164
https://github.com/golang/go/blob/master/src/runtime/msize.go
Related
This question already has answers here:
How the slice is enlarged by append? Is the capacity always doubled?
(2 answers)
Closed 3 years ago.
Running the example The Go Tour on server (currently on version 1.12.7), I find the capacity of slice doubling to the next power of 2, if the new slice length is larger than current backing array's length.
If I run the same program on my machine (version 1.10.3 on windows), the slice capacity changes to next multiple of two.
Why are they different? Is it because of Go version or run-time implementations? Is the capacity change deterministic?
The output on remote server is this
len=0 cap=0 []
len=1 cap=2 [0]
len=2 cap=2 [0 1]
len=5 cap=8 [0 1 2 3 4]
The output on local machine is this
len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]
This is the code for reference
package main
import "fmt"
func main() {
var s []int
printSlice(s)
// append works on nil slices.
s = append(s, 0)
printSlice(s)
// The slice grows as needed.
s = append(s, 1)
printSlice(s)
// We can add more than one element at a time.
s = append(s, 2, 3, 4)
printSlice(s)
}
func printSlice(s []int) {
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
TL;DR: It depends on the size of the elements stored in the array
The implementation can be seen here:
https://github.com/golang/go/blob/master/src/runtime/slice.go
But as you can see looking into the history it can't be relied to stay the same over time.
That might also explain the difference you may note on different versions of Go.
Making some tests show how a 0-size struct will increment capacity by just 1 element, and int or string will duplicate on each growth, and a 3-byte struct "roughly" doubles on each growth.
You can execute code like this using different types to see these different cases in action:
arr := []struct{}{}
oldCap := 0
for i := 0; i < 100; i++ {
arr = append(arr, struct{}{})
if cap(arr) != oldCap {
oldCap = cap(arr)
fmt.Println("arr", cap(arr))
}
}
Playground showing the cases described above:
https://play.golang.org/p/OKtCFskbp2t
According to information from Go slice internal The implementation behaviour of append is as follows.
It is just a multiple of the (len(source slice) + len(new data)) * 2
func AppendByte(slice []byte, data ...byte) []byte {
m := len(slice)
n := m + len(data)
if n > cap(slice) { // if necessary, reallocate
// allocate double what's needed, for future growth.
newSlice := make([]byte, (n+1)*2)
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:n]
copy(slice[m:n], data)
return slice
}
So I am trying to write a method that takes two slices, flips both of them and then gives them to each other.
Ex.
s1 = {1,2,3,4,5}
s2 = {6,7,8,9,10}
Should return:
s1 = {10,9,8,7,6}
s2 = {5,4,3,2,1}
Here is my code:
package main
import(
"fmt"
)
func main(){
f:= [5]int{1,2,3,4,5}
h:= [5]int{6,7,8,9,10}
var sliceF []int = f[0:5]
var sliceH []int = h[0:5]
fmt.Println(reverseReverse(sliceF,sliceH))
}
func reverseReverse(first []int, second []int) ([]int, []int){
//creating temp arrays to hold the traversed arrays before swapping.
var tempArr1 []int = first
var tempArr2 []int = second
//count is used for counting up the tempArrays in the correct order in the For loops
var count int= 0
//goes through the first array and sets the values starting from the end equal to the temp array
//which increases normally from left to right.
for i :=len(first)-1; i>=0;i--{
tempArr1[count] = first[i]
fmt.Println(i)
count++
}
count =0
//same as first for loop just on the second array
for i :=len(second)-1; i>=0;i--{
tempArr2[count] = second[i]
count++
}
//trying to replace the values of the param arrays to be equal to the temp arrays
first=tempArr2
second = tempArr1
//returning the arrays
return first,second
}
When run here is the output:
4
3
2
1
0
[10 9 8 9 10]
[5 4 3 4 5]
*Not I included a print statement in the for loop to check if the index is decreasing properly.
I understand there are better ways to do this but for proof of concept I want to use a for loop.
Any help appreciated. I am new to go and tend to have java habits so I assume somehow my problem is related to that.
This can be done much simpler by realizing there's no need to actually swap the individual elements. Instead, reverse each array and swap their order. Much simpler!
func reverseReverse( a, b []int ) ([]int, []int) {
return reverse(b), reverse(a)
}
func reverse( a []int ) []int {
end := len(a) - 1
// Allocate a new array slice of the same length to copy to.
ret := make( []int, len(a) )
// Copy each element of a into ret, reversed.
for i := range a {
ret[end-i] = a[i]
}
return ret
}
With that revelation, there's little need for the very specialized reverseReverse function. Swap the order yourself.
fmt.Println(reverse(sliceH), reverse(sliceF))
Note that if you just want to take a slice of an array, it's sufficient to write sliceH []int := h[:] without specifying the start and end. The start is assumed to be 0 and the end is the end. Also note there's no need to declare the type, := takes care of that for you.
Even better, you can declare and initialize them directly.
sliceF:= []int{1,2,3,4,5}
sliceH:= []int{6,7,8,9,10}
Short answer:
tempArr1[count] = first[i]
This line is logically identical to:
first[count] = first[i]
Detailed answer:
x := [5]int{} and x := []int{} are in fact two very different assignments. In the first case x is actually a static array. In the second case x is a slice which is in fact a data structure which has a length, capacity and a pointer to the underlying array. Therefore, var tempArr1 []int = first means copy the pointer to the underlying array of first into the tempArr1, so any modification to first[i] will be reflected in tempArr1 and vice versa
For example,
package main
import "fmt"
func reverse(s []int) []int {
for i := 0; i < len(s)/2; i++ {
s[i], s[len(s)-1-i] = s[len(s)-1-i], s[i]
}
return s
}
func main() {
s1, s2 := []int{1, 2, 3, 4, 5}, []int{6, 7, 8, 9, 10}
fmt.Println(s1, s2)
s1, s2 = reverse(s2), reverse(s1)
fmt.Println(s1, s2)
}
Output:
[1 2 3 4 5] [6 7 8 9 10]
[10 9 8 7 6] [5 4 3 2 1]
Suppose I have a slice slice of type int. While declaring, I set the third argument to size, which I believe reserves memory for at least size ints by setting the cap parameter of the slice.
slice:=make([]int,0,size)
Now, suppose I have an integer variable value. To add it to the slice at the end, I use
slice=append(slice,value)
If the number of elements currently in the slice is less than size, then there will be no need to copy the entire underlying array to a new location in order to add the new element.
Further, if I want to prepend value to slice, as suggested here and here, I use
slice=append([]int{value},slice...)
My question is, what happens in this case? If the number of elements is still less than size, how are the elements stored in the memory? Assuming a contiguous allocation when the make() function was invoked, are all existing elements right shifted to free the first space for value? Or is memory reallocated and all elements copied?
The reason for asking is that I would like my program to be as fast as possible, and would like to know if this is a possible cause for slowing it down. If it is so, is there any alternative way of prepending that would be more time efficient?
With reslicing and copying
The builtin append() always appends elements to a slice. You cannot use it (alone) to prepend elements.
Having said that, if you have a slice capacity bigger than length (has "free" space after its elements) to which you want to prepend an element, you may reslice the original slice, copy all elements to an index one higher to make room for the new element, then set the element to the 0th index. This will require no new allocation. This is how it could look like:
func prepend(dest []int, value int) []int {
if cap(dest) > len(dest) {
dest = dest[:len(dest)+1]
copy(dest[1:], dest)
dest[0] = value
return dest
}
// No room, new slice need to be allocated:
// Use some extra space for future:
res := make([]int, len(dest)+1, len(dest)+5)
res[0] = value
copy(res[1:], dest)
return res
}
Testing it:
s := make([]int, 0, 5)
s = append(s, 1, 2, 3, 4)
fmt.Println(s)
s = prepend(s, 9)
fmt.Println(s)
s = prepend(s, 8)
fmt.Println(s)
Output (try it on the Go Playground):
[1 2 3 4]
[9 1 2 3 4]
[8 9 1 2 3 4]
Note: if no room for the new element, since performance does matter now, we didn't just do:
res := append([]int{value}, dest...)
Because it does more allocations and copying than needed: allocates a slice for the literal ([]int{value}), then append() allocates a new when appending dest to it.
Instead our solution allocates just one new array (by make(), even reserving some space for future growth), then just set value as the first element and copy dest (the previous elements).
With linked list
If you need to prepend many times, a normal slice may not be the right choice. A faster alternative would be to use a linked list, to which prepending an element requires no allocations of slices / arrays and copying, you just create a new node element, and you designate it to be the root by pointing it to the old root (first element).
The standard library provides a general implementation in the container/list package.
With manually managing a larger backing array
Sticking to normal slices and arrays, there is another solution.
If you're willing to manage a larger backing array (or slice) yourself, you can do so by leaving free space before the slice you use. When prepending, you can create a new slice value from the backing larger array or slice which starts at an index that leaves room for 1 element to be prepended.
Without completeness, just for demonstration:
var backing = make([]int, 15) // 15 elements
var start int
func prepend(dest []int, value int) []int {
if start == 0 {
// No more room for new value, must allocate bigger backing array:
newbacking := make([]int, len(backing)+5)
start = 5
copy(newbacking[5:], backing)
backing = newbacking
}
start--
dest = backing[start : start+len(dest)+1]
dest[0] = value
return dest
}
Testing / using it:
start = 5
s := backing[start:start] // empty slice, starting at idx=5
s = append(s, 1, 2, 3, 4)
fmt.Println(s)
s = prepend(s, 9)
fmt.Println(s)
s = prepend(s, 8)
fmt.Println(s)
// Prepend more to test reallocation:
for i := 10; i < 15; i++ {
s = prepend(s, i)
}
fmt.Println(s)
Output (try it on the Go Playground):
[1 2 3 4]
[9 1 2 3 4]
[8 9 1 2 3 4]
[14 13 12 11 10 8 9 1 2 3 4]
Analysis: this solution makes no allocations and no copying when there is room in the backing slice to prepend the value! All that happens is it creates a new slice from the backing slice that covers the destination +1 space for the value to be prepended, sets it and returns this slice value. You can't really get better than this.
If there is no room, then it allocates a larger backing slice, copies over the content of the old, and then does the "normal" prepending.
With tricky slice usage
Idea: imagine that you always store elements in a slice in backward order.
Storing your elements in backward order in a slice means a prepand becomes append!
So to "prepand" an element, you can simply use append(s, value). And that's all.
Yes, this has its limited uses (e.g. append to a slice stored in reverse order has the same issues and complexity as a "normal" slice and prepand operation), and you lose many conveniences (ability to list elements using for range just to name one), but performance wise nothing beats prepanding a value just by using append().
Note: iterating over the elements that stores elements in backward order has to use a downward loop, e.g.:
for i := len(s) - 1; i >= 0; i-- {
// do something with s[i]
}
Final note: all these solutions can easily be extended to prepend a slice instead of just a value. Generally the additional space when reslicing is not +1 but +len(values), and not simply setting dst[0] = value but instead a call to copy(dst, values).
The "prepend" call will need to allocate an array and copy all elements because a slice in Go is defined as a starting point, a size and an allocation (with the allocation being counted from the starting point).
There is no way a slice can know that the element before the first one can be used to extend the slice.
What will happen with
slice = append([]int{value}, slice...)
is
a new array of a single element value is allocated (probably on stack)
a slice is created to map this element (start=0, size=1, alloc=1)
the append call is done
append sees that there is not enough room to extend the single-element slice so allocates a new array and copies all the elements
a new slice object is created to refer to this array
If appending/removing at both ends of a large container is the common use case for your application then you need a deque container. It is unfortunately unavailable in Go and impossible to write efficiently for generic contained types while maintaining usability (because Go still lacks generics).
You can however implement a deque for your specific case and this is easy (for example if you have a large container with a known upper bound may be a circular buffer is all you need and that is just a couple of lines of code away).
I'm very new to Go, so may be the following is very bad Go code... but it's an attempt to implement a deque using a growing circular buffer (depending on the use case this may be or may be not a good solution)
type Deque struct {
buffer []interface{}
f, b, n int
}
func (d *Deque) resize() {
new_buffer := make([]interface{}, 2*(1+d.n))
j := d.f
for i := 0; i < d.n; i++ {
new_buffer[i] = d.buffer[j]
d.buffer[j] = nil
j++
if j == len(d.buffer) {
j = 0
}
}
d.f = 0
d.b = d.n
d.buffer = new_buffer
}
func (d *Deque) push_back(x interface{}) {
if d.n == len(d.buffer) {
d.resize()
}
d.buffer[d.b] = x
d.b++
if d.b == len(d.buffer) {
d.b = 0
}
d.n++
}
func (d *Deque) push_front(x interface{}) {
if d.n == len(d.buffer) {
d.resize()
}
if d.f == 0 {
d.f = len(d.buffer)
}
d.f--
d.buffer[d.f] = x
d.n++
}
func (d *Deque) pop_back() interface{} {
if d.n == 0 {
panic("Cannot pop from an empty deque")
}
if d.b == 0 {
d.b = len(d.buffer)
}
d.b--
x := d.buffer[d.b]
d.buffer[d.b] = nil
d.n--
return x
}
func (d *Deque) pop_front() interface{} {
if d.n == 0 {
panic("Cannot pop from an empty deque")
}
x := d.buffer[d.f]
d.buffer[d.f] = nil
d.f++
if d.f == len(d.buffer) {
d.f = 0
}
d.n--
return x
}
I am using golang. Here is my code:
func main() {
quanPailie([]int{1, 2})
}
func quanPailie(nums []int) [][]int {
COUNT := len(nums)
//only one item
if COUNT == 1 {
return [][]int{nums}
}
insertItem(quanPailie(nums[:COUNT-1]), nums[COUNT-1])
return [][]int{}
}
func insertItem(res [][]int, insertNum int) {
fmt.Println("insertItem,res:", res, "insertNum", insertNum) //insertItem,res: [[1]] insertNum 2
for _, v := range res {
for i := 0; i < len(v); i++ {
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
fmt.Println("ccc", c)
}
}
}
What makes me very confused is the output:
===before,v: [1]
===after,v: [2]
Why did the value of v change? Hope someone can help me. Thanks a lot.
Go playground: https://play.golang.org/p/wITYsGpX7U
EDIT:
Thanks for icza's great help, I think I have understood this problem.
And, here is a simple code to show this issue.
func test1() {
nums := []int{1, 2, 3}
_ = append(nums[:2], 4)
fmt.Println("test1:", nums)
//nums changes because the cap is big enought, the original array is modified.
}
func test2() {
nums := []int{1, 2, 3}
c := append(nums[:2], []int{4, 5, 6}...)
fmt.Println("test2:", nums)
fmt.Println("cc:", c)
//nums dont't change because the cap isn't big enought.
//a new array is allocated while the nums still points to the old array.
//Of course, the return value of append points to the new array.
}
Go playground: https://play.golang.org/p/jBNFsCqUn3
This is the code in question:
fmt.Println("===before,v:", v)
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v)
You ask why v changes between the 2 Println() statements.
Because you are using the builtin append() function, quoting from its doc:
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice.
So if the slice you append to has enough room (capacity) to accomodate the elements you want to append, no new slice will be allocated, instead the destination slice will be re-sliced (which will use the same underlying array) and append will happen in that.
Let's check the capacity:
fmt.Println("===before,v:", v, cap(v))
c := append(v[:i], append([]int{insertNum}, v[i:]...)...)
fmt.Println("===after,v:", v, cap(v))
Output:
===before,v: [1] 2
===after,v: [2] 2
The v slice has a capacity of 2. When for loop starts, i=0, v[:i] is v[:0] which is an empty slice (but has capacity 2) and so appending 1 or 2 elements will not allocate a new array/slice, it will be done "in place". This "in place" is the 0th element of v, since v[:i] is shorthand for v[0:i]. Hence the elements will be appended starting from v[0] in the underlying array which is shared, so the element denoted by v[0] will change.
Note that slicing a slice results in a slice which shares its underlying backing array with the original slice (does not make a copy of the elements).
If you want to avoid this, use or allocate a new slice, copy original content and append to the new slice, e.g.:
src := []int{1, 2}
c := make([]int, len(src))
copy(c, src)
// Append something:
c = append(c, 3, 4)
fmt.Println(src) // [1 2] - src doesn't change
fmt.Println(c) // [1 2 3 4]
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?
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).
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
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
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
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
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]
Sweet, Simple, Performant, No need to be careful of length, No Memory overlap, Different copies
slice2 := append([]int{}, slice1...)
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]
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
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...)
}