I want to copy an array but the copied array always makes changes to the original array as well. Why is this the case?
Below is a code example of the issue:
package main
import (
"fmt"
)
func main() {
array := make([]int, 2)
for i := range array {
array[i] = 0
}
oldArray := array
array[0] = 3 // why does this also change oldArray?
oldArray[1] = 2 // why does this also change the original array?
fmt.Println(oldArray, array)
// I expected [0,2], [3,0]
// but it returns [3,2], [3,2]
}
I tried to initiate the variable oldArray before array but the result is the same.
In your example code, you're working with slices, not arrays.
From the slice documentation:
A slice is a descriptor for a contiguous segment of an underlying array and provides access to a numbered sequence of elements from that array.
When you assign a slice to a variable, you're creating a copy of that descriptor and therefore dealing with the same underlying array. When you're actually working with arrays, it has the behavior you're expecting.
Another snippet from the slice documentation (emphasis mine):
A slice, once initialized, is always associated with an underlying array that holds its elements. A slice therefore shares storage with its array and with other slices of the same array; by contrast, distinct arrays always represent distinct storage.
Here's a code sample (for the slices, the memory address of the first element is in parentheses, to clearly point out when two slices are using the same underlying array):
package main
import (
"fmt"
)
func main() {
// Arrays
var array [2]int
newArray := array
array[0] = 3
newArray[1] = 2
fmt.Printf("Arrays:\narray: %v\nnewArray: %v\n\n", array, newArray)
// Slices (using copy())
slice := make([]int, 2)
newSlice := make([]int, len(slice))
copy(newSlice, slice)
slice[0] = 3
newSlice[1] = 2
fmt.Printf("Slices (different arrays):\nslice (%p): %v \nnewSlice (%p): %v\n\n", slice, slice, newSlice, newSlice)
// Slices (same underlying array)
slice2 := make([]int, 2)
newSlice2 := slice2
slice2[0] = 3
newSlice2[1] = 2
fmt.Printf("Slices (same array):\nslice2 (%p): %v \nnewSlice2 (%p): %v\n\n", slice2, slice2, newSlice2, newSlice2)
}
Output:
Arrays:
array: [3 0]
newArray: [0 2]
Slices (different arrays):
slice (0xc000100040): [3 0]
newSlice (0xc000100050): [0 2]
Slices (same array):
slice2 (0xc000100080): [3 2]
newSlice2 (0xc000100080): [3 2]
Go Playground
use copy function.
oldArray := make([]int, len(array))
copy(oldArray, array)
https://play.golang.org/p/DsLJ2PDIy_N
Related
I was reading this article which explains how slices in Go are implemented under the hood:
https://medium.com/swlh/golang-tips-why-pointers-to-slices-are-useful-and-how-ignoring-them-can-lead-to-tricky-bugs-cac90f72e77b
At the end of the article is this snippet of Go code:
func main() {
slice:= make([]string, 1, 3)
func(slice []string){
slice=slice[1:3]
slice[0]="b"
slice[1]="b"
fmt.Print(len(slice))
fmt.Print(slice)
}(slice)
fmt.Print(len(slice))
fmt.Print(slice)
}
My first guess was this would print:
2 [b b]3 [ b b]
In fact it prints:
2[b b]1[]
Which suggests that when anonymous function creates a new local slice by slicing the one passed to it as argument causes a new underlying array to be allocated for the slice. I've confirmed this with this modified version of code:
func main() {
slice := make([]string, 1, 3)
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in main: %p\n", unsafe.Pointer(hdr.Data))
func(slice []string) {
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in func before slicing: %p\n", unsafe.Pointer(hdr.Data))
slice = slice[1:3]
slice[0] = "b"
slice[1] = "b"
hdr = (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Printf("adress of underlying array in func after slicing: %p\n", unsafe.Pointer(hdr.Data))
fmt.Print(len(slice))
fmt.Println(slice)
}(slice)
fmt.Print(len(slice))
fmt.Println(slice)
}
Which prints:
adress of underlying array in main: 0xc0000121b0
adress of underlying array in func before slicing: 0xc0000121b0
adress of underlying array in func after slicing: 0xc0000121c0
2[b b]
1[]
My question is: why is the slicing operation in anonymous function causing a new array to be allocated? My understanding was that if in main we are creating a slice with capacity 3, an underlying array with length 3 is created and when anonymous function is manipulating the slice, it is manipulating the same underlying array.
As mkopriva mentioned, the reslicing does not reallocate anything. Reallocation happens only when appending new values would exceed the slice's capacity (source).
The output you got is because the elements you are "able to see" in a slice (including when you print it) depend on its length:
The number of elements is called the length of the slice and is never negative. [...] The length of a slice s can be discovered by the built-in function len;
The original slice constructed with slice := make([]string, 1, 3), has length=1, so when you print it, the output will be the one element at position 0 of the backing array, which is an empty string.
With this code:
slice = slice[1:3]
slice[0] = "b"
slice[1] = "b"
you are effectively mutating the elements at position 1 and 2 of the backing array, none of which is an element of the original slice.
If you reslice the original slice up to capacity — thus extending its length, it will print what you expect:
slice = slice[:cap(slice)]
fmt.Println(len(slice)) // 3
fmt.Println(slice) // [ b b]
// ^ first is empty string
A basic question that I'm struggling to find an answer for as there are a lot of answers about how to join two slices using the append function and the spread operator which erroneously use the word 'array'.
I am new to Go and have made the assumption that using sized arrays is good practice where the size is known. However I am struggling to work with arrays as I can't figure out how to do simple operations such as concatenation. Here is some code.
var seven [7]int
five := [5]int{1,2,3,4,5}
two := [2]int{6,7}
//this doesn't work as both the inputs and assignment are the wrong type
seven = append(five,two)
//this doesn't work as the assignment is still the wrong type
seven = append(five[:],two[:])
//this works but I'm not using arrays anymore so may as well use slices everywhere and forget sizing
seven2 := append(five[:],two[:])
As far as I can see I can either just give up on arrays and use slices exclusively or I could write a loop to explicitly construct the new array. Is there a third option?
append() can only be used to append elements to a slice. If you have an array, you can't pass that directly to append().
What you may do is slice the array, so you get a slice (which will use the array as its backing store), and you can use that slice as the target and source of elements.
For example:
s := seven[:0]
s = append(s, five[:]...)
s = append(s, two[:]...)
fmt.Println(seven)
This will print (try it on the Go Playground):
[1 2 3 4 5 6 7]
Also note that since append() returns the resulting slice, it's possible to write all this in one line:
_ = append(append(seven[:0], five[:]...), two[:]...)
(Storing the result is not needed here because we have and want to use only the backing array, but in general that is not the case.)
This outputs the same, try it on the Go Playground. Although this isn't very readable, so it's not worth compacting it into a single line.
Although when you have the target array, "appending" arrays is nothing more than copying them to the target, to the proper position. For that, you may use the builtin copy() function too. Note that the copy() function also accepts only slices, so you have to slice the arrays here too.
copy(seven[:], five[:])
copy(seven[len(five):], two[:])
fmt.Println(seven)
This will output the same. Try this one on the Go Playground.
You can use copy
copy(seven[:], five[:])
copy(seven[5:], two[:])
fmt.Printf("%v\n", seven)
> [1 2 3 4 5 6 7]
You can concatenate two arrays in go using copy function
package main
import "fmt"
func main() {
five := [5]int{1, 2, 3, 4, 5}
two := [2]int{6, 7}
var n [len(five) + len(two)]int
copy(n[:], five[:])
copy(n[len(five):], two[:])
fmt.Println(n)
}
https://blog.golang.org/go-slices-usage-and-internals
Golang runtime used to check whether current index exceeds the maximum possible.
On the side of array, it look ups its type (which contain its len and reference to the element type), because that's type, that can be registered only at compile time.
// each array mention with unique size creates new type
array := [5]byte{1,2,3,4,5}
On the side of slice, it look ups their header which looks like:
type slice {
data *byte
len int
cap int // capacity, the maximum possible index
}
As you can see, any slice is a single structure with data and len, cap fields, meanwhile array is just single pointer to data (*byte).
When you trying to convert array to slice, it just creates slice header and fills fields with:
slice := array[:]
==
slice := Slice{}
slice.data = array
slice.len = type_of(array).len
slice.cap = type_of(array).len
you can do that simply by converting array into slice:
arr1 := [...]int {1,2,3,}
arr2 := [...]int {4,5,6, }
//arr3 = arr1 + arr2 // not allowed
// converting arrays into slice
slc_arr1, slc_arr2 := arr1[:], arr2[:]
slc_arr3 := make([]int, 0)
slc_arr3 = append(slc_arr1, slc_arr2...)
fmt.Println(slc_arr3) // [1 2 3 4 5 6]
There is a more general way of appending an array of any type(once Golang has generics, but for now this solution is specific to strings. Just change the type as appropriate). The notion of Fold comes from Functional Programming. Note I have also included a filter function which also uses Fold. The solution is not stack safe but in many cases that does not matter. It can be made stack safe with trampolining. At the end is an example of its usage.
func FoldRightStrings(as, z []string, f func(string, []string) []string) []string {
if len(as) > 1 { //Slice has a head and a tail.
h, t := as[0], as[1:len(as)]
return f(h, FoldRightStrings(t, z, f))
} else if len(as) == 1 { //Slice has a head and an empty tail.
h := as[0]
return f(h, FoldRightStrings([]string{}, z, f))
}
return z
}
func FilterStrings(as []string, p func(string) bool) []string {
var g = func(h string, accum []string) []string {
if p(h) {
return append(accum, h)
} else {
return accum
}
}
return FoldRightStrings(as, []string{}, g)
}
func AppendStrings(as1, as2 []string) []string {
var g = func(h string, accum []string) []string {
return append(accum, h)
}
return FoldRightStrings(as1, as2, g)
}
func TestAppendStringArrays(t *testing.T) {
strings := []string{"a","b","c"}
bigarray := AppendStrings(AppendStrings(strings, strings),AppendStrings(strings, strings))
if diff := deep.Equal(bigarray, []string{"a","b","c","c","b","a","a","b","c","c","b","a"}); diff != nil {
t.Error(diff)
}
}
1)
For below slice declaration,
var a [][3]string
creates a single slice(a) that points to array of 3 strings, len(a) = 3 and cap(a) =3 but not cap(a) >= 3
a = make([][3]string, 5)
creates 5 slices, where each slice(say a[0]) points to array of 3 strings, len(a[0]) = 3 and cap(a[0]) = 3
2)
For slice declaration,
var b [][3][6]string
creates a single slice(b) that points to 3 arrays of 6 strings each, where len(b) = 3 and cap(b) =3
b = make([][3][6]string, 5)
creates 5 slices, where each slice(say b[0]) points to 3 arrays of 6 strings each
In both cases, after making slices, I said, creates 5 slices,
Considering the syntax, below are my two questions,
a = make([][3]string, 5)
My question:
1)
Is it 5 slices, where each slice(a[0]) is array of 3 strings?
or
Is it a single slice(a) pointing to 5 arrays of 3 strings each?
2)
How do I know the type of an element? slice or array?
1) In such scenarios, "array of" terminology has to be cautiously used.
2) There are no implicit pointers involved in GO unlike C
Slices and arrays are two different types: an array in memory is a contiguous sequences of values of the same type. In Go a type has a fixed size. The very same concept is present for example in C++ and
int x[5]; // C and C++
x [5]int // Go
are basically the same thing (not 100% the same because arrays in C and C++ are "second class citizens" and behave strangely in a few places, Go is more uniform on that).
A slice in Go is instead a "view" of a portion of an array, and is more or less equivalent to a C++ structure with a pointer to the first element, a number of used elements (relative to first) and a number of available elements (relative to first)
// C++
template<typename T>
struct Slice {
T *first;
int size, capacity;
};
Slice<int> x{nullptr, 0, 0};
// Go
var x []int
The Make special function is able to create slices associated to newly created arrays, given size and optional capacity:
// C++
template<typename T>
Slice<T> make(int size, int capacity=-1) {
if (capacity == -1) capacity = size;
return Slice<T>{new T[capacity], size, capacity};
}
// Go
x := Make([]int, size, capacity)
Slices can be efficiently passed around in O(1) (independently on size/capacity) and you can also take a sub-slice of a slice in O(1)... note that Go is garbage collected, doing the same in C++ would require some extra work (for example also keeping a pointer to the original array object and its size in the slice).
You can of course have arrays of slices, slices of slices, slices of arrays and arrays of arrays. Note however that in Go Make is used only to create slices (and maps) not for arrays...
x := Make([][2]int, 3) // a slice of 3 arrays of 2 ints each
// Same thing (except for content)
y := [][2]int{[2]int{1, 2},
[2]int{3, 4},
[2]int{5, 6}}
// An array of 3 slices of two ints each
z := [3][]int{[]int{1, 2},
[]int{3, 4},
[]int{5, 6}}
While for example both y and z in the playground would look the same [[1, 2], [3, 4], [5, 6]] when using fmt.Println, they are very different types, for example you can add a new pair to y with
y = append(y, [2]int{7, 8}) // now [[1, 2], [3, 4], [5, 6], [7, 8]]
and you can instead increase the length of first element of z with
z[0] = append(z[0], 99) // now [[1, 2, 99], [3, 4], [5, 6]]
but you cannot add new elements to z (it's an array) and you cannot extend an element of y (because elements are arrays)
1) Considering the syntax,
a = make([][3]string, 5)
Is it 5 slices, where each slice(a[0]) is array of 3 strings?
or
Is it a single slice(a) pointing to 5 arrays of 3 strings each?
Its a single slice with 5 elements where each element is an array of 3 strings.
2)
How do I know the type of an element? slice or array?
Slices and arrays are different types. You can not interchangeably assign arrays and slices to the same variable, hence if the declaration declares it as an array, its an array, if it declares it as a slice, its a slice. If it has a number in the brackets ([5]string) its an array, if the brackets are empty ([]string) its a slice.
2) For slice declaration,
var b [][3][6]string
creates a single slice(b) that points to 3 arrays of 6 strings each,
where len(b) = 3 and cap(b) =3
b = make([][3][6]string, 5)
creates 5 slices, where each slice(say b[0]) points to 3 arrays of 6 strings each
No. Former code just declares a variable, it doesn't hold a slice yet. Latter code creates one slice with five elements.
Trying to teach myself and finding it hard to find examples, and my brain's in a knot already. Very unsure about 3 and 4 and need help for making 5 work.
package main
import "fmt"
func main () {
println("0. Array:")
var a = [...]int{4,5,6,7,8,9} //assign
fmt.Println(a,"\n")
println("1. Slice:")
var as []int
as = a[:] //assign
fmt.Println(as,"\n")
println("2. Array of arrays:")
var b [4][len(a)]int
for i:= range b { //assign
b[i]=a
}
fmt.Println(b,"\n")
println("3. Array of slices:")
var d [len(b)][]int
for i:= range b { // assign
d[i] = b[i][:] //does this really work?
}
fmt.Println(d,"\n")
println("4. Slice of arrays:")
var c [][len(a)]int
c = b[:][:] // assign, does this really work?
fmt.Println(c,"\n")
println("5. Slice of slices:")
var e [][]int
// e = c // ???
fmt.Println(e,"\n")
}
Part 3 works.
Part 4 contains an unnecessary [:].
println("4. Slice of arrays:")
var c [][len(a)]int
c = b[:] // one [:], not two
fmt.Println(c, "\n")
b[:] is evaluated as a slice where each element is a [len(a)]int. If you add another [:], you are slicing the slice again. Since for any slice s, s[:] == s, it is a no op.
Part 5, you can slice your array of slices.
println("5. Slice of slices:")
var e [][]int
e = d[:]
fmt.Println(e, "\n")
I posted a complete example at http://play.golang.org/p/WDvJXFiAFe.
The answer to "does this really work?" depends on what you are expecting. Consider this example at http://play.golang.org/p/7Z5hKioTI_
package main
import "fmt"
func main() {
fmt.Println("0. Array:")
var a = [...]int{4, 5, 6, 7, 8, 9} //assign
fmt.Println(a, "\n")
fmt.Println("1. Slice:")
var as []int
as = a[:] //assign
fmt.Println(as, "\n")
fmt.Println("new slice:")
ns := make([]int, len(a))
copy(ns, a[:])
fmt.Print(ns, "\n\n")
fmt.Println("modifying array...")
a[0] = 10
fmt.Print("array is now:\n", a, "\n\n")
fmt.Print("slice is now:\n", as, "\n\n")
fmt.Print("new slice is still:\n", ns, "\n")
}
It shows how slices have an underlying array, and that the examples in your OP make slices using the same underlying array. If you want slices to have independent contents, you must make new slices and copy the data. (or there are tricks with append...)
Also as a side note, println sends data to stderr not stdout, and formats some data types differently than fmt.Println. To avoid confusion, it's best to stay in the habit of using fmt.Println.
Having read the following at http://golang.org/doc/effective_go.html#arrays...
Arrays are values. Assigning one array to another copies all the
elements.
In particular, if you pass an array to a function, it will receive a copy
of the array, not a pointer to it.
... I expect in the following code that arr2 to be distinct from arr, and main()'s arr to be distinct from shuffle()'s arr. Can someone please explain why the following code shuffles arr2? I know Go is still a young language; perhaps the treatment of arrays has changed?
package main
import (
"fmt"
"rand"
"time"
)
func shuffle(arr []int) {
rand.Seed(time.Nanoseconds())
for i := len(arr) - 1; i > 0; i-- {
j := rand.Intn(i)
arr[i], arr[j] = arr[j], arr[i]
}
}
func main() {
arr := []int{1, 2, 3, 4, 5}
arr2 := arr
shuffle(arr)
for _, i := range arr2 {
fmt.Printf("%d ", i)
}
}
I think your problem is that you're confusing arrays and slices.
Arrays are fixed-length lists of values. You're actually not using any arrays in your example. Arrays can be declared a few ways:
arr1 := [3]int{1, 2, 3} // an array of 3 integers, 1-3
arr2 := [...]int{1, 2, 3} // same as the previous line, but we're letting
// the compiler figure out the size of the array
var arr3 [3]int // a zeroed out array of 3 integers
You're using slices. A slice is a reference to an underlying array. There are a few ways to allocate new slices:
slice1 := []int{1, 2, 3} // a slice of length 3 containing the integers 1-3
slice2 := make([]int, 3) // a slice of length 3 containing three zero-value integers
slice3 := make([]int, 3, 5) // a slice of length 3, capacity 5 that's all zeroed out
Any other slice assignments are just duplicating a reference to an array.
Now that we've established that, the line
arr := []int{1, 2, 3, 4, 5}
creates a slice referencing an anonymous underlying array that contains the numbers 1-5.
arr2 := arr
duplicates that reference -- it does not copy the underlying array. So there's one underlying array and two references to it. That's why the contents of arr2 change when you modify the contents of arr. They're referencing the same array.