I am working on a sample program in golang which is as follows
package main
import (
"fmt"
)
type thing [2]byte
func walk(things []thing, idx int) []byte {
var match []byte
for i, thing := range things {
if i == idx {
match = thing[:]
}
}
return match
}
func main() {
ta := []thing{ thing{'W','A'}, thing{'O','R'} }
m := walk(ta, 0)
tb := []thing{ thing{'C','A'}, thing{'W','Y'}, thing{'N','V'} }
n := walk(tb, 1)
fmt.Printf("m = %s
", m)
fmt.Printf("n = %s
", n)
}
The output is:
m = OR
n = NV
I am not sure why this is the case when type thing [2]byte is an array of size 2 and ta is type []thing.
Now when you write the same code as this using [][]byte
package main
import (
"fmt"
)
func walk(things [][]byte, idx int) []byte {
var match []byte
for i, thing := range things {
if i == idx {
match = thing[:]
}
}
return match
}
func main() {
ta := [][]byte{[]byte{'W', 'A'}, []byte{'O', 'R'}}
m := walk(ta, 0)
tb := [][]byte{[]byte{'C', 'A'}, []byte{'W', 'Y'}, []byte{'N', 'V'}}
n := walk(tb, 1)
fmt.Printf("m = %s
", m)
fmt.Printf("n = %s
", n)
}
The output is
m = WA
n = WY
I am confused by these different behaviours of slices? Printing out ta[:]
when ta is type thing []byte
and ta[:] when ta is [][]byte
is the same
Two cases are not the same.
In the first case, you are working with [][2]byte
(slice of arrays) not with [][]byte
(slice of slices).
var match []byte
for i, thing := range things { // (2) change array on each iteration
fmt.Printf("Thing %v", thing)
if i == idx {
match = thing[:] // (1) slice refers to array
}
}
return match // (3) here match slice refers to the last item of things
One of solutions here is adding break
statement after match = thing[:]
. It ends the loop and match
will refer to expected array.
Just to clarify what is the actual issue here, the problem is that you are creating an slice that refers to an array that is overwritted in each n-iteration with the values of the correspondant n-th element of the slice of 2 bytes array. So if you don't stop iterating the slice will get the values of the last element.
Using a for .. := range
"hides" a little bit this fact. I guess if you write the same code with a for i;i<n;i++
, you could get a better understanding of what is actually happening: https://play.golang.org/p/z3hCskZ9ezV
In one case, an individual thing
is an array ([2]byte
), and in the other, it's a slice ([]byte
). In the first case, you're slicing an array into match
, which gives you a new slice pointing at your loop iteration variable. In the second case, you're re-slicing an existing slice, so your new slice points at that slice's underlying array, even after the loop changes out the local slice variable.
Because your loop keeps running after you find your match, you find your match, slice it, then keep iterating, changing the value of the local loop iterator. If you did:
if i == idx {
return thing[:]
}
instead, the issue disappears: https://play.golang.org/p/Uq4DbEGlGX8