-2

I ran into this simple Golang code and was surprised by Go's behavior here. Can someone explain what is going on here, and how to write the below code correctly?

As you can see, I have a map, where the key is an array of int. I add a couple of values and then I loop through the map, convert each key to a slice and append each key to an object of type [][]int.

func test() {
    myMap := make(map[[3]int]bool)
    myMap[[3]int{1, 2, 3}] = true
    myMap[[3]int{0, 5, 4}] = true
    myMap[[3]int{9, 7, 1}] = true
    myMap[[3]int{0, 2, 8}] = true
    array := [][]int{}

    for val := range myMap {
        array = append(array, val[:])
    }
    fmt.Println(array)
}

I was expecting the last line to print [[1,2,3], [0,5,4], [9,7,1], [0,2,8]], however, to my surprise it prints [[0 2 8] [0 2 8] [0 2 8] [0 2 8]], or [[9 7 1] [9 7 1] [9 7 1] [9 7 1]], or some other variation containing only one of the keys multiple times.

My go version is 1.16.5

lajosdeme
  • 2,189
  • 1
  • 11
  • 20

3 Answers3

4

In a for-loop, the loop variables are overwriten at every iteration. That is, the val is an array, and for each iteration, the contents of val are overwritten with the next item in the map. Since you added slices (which are simply views over an array), all the slices have val as the backing array, and they all have the same contents, namely, whatever the last element iterated.

To fix, copy the array:

    for val := range myMap {
        val:=val
        array = append(array, val[:])
    }
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
3

You're appending the loop iterator variable each time, which is updated each iteration. You need to append a locally-scoped copy instead:

for val := range myMap {
    v := val
    array = append(array, v[:])
}
Adrian
  • 42,911
  • 6
  • 107
  • 99
1

Based on suggestion of Adrian, recreating your code with a simple program as follows:

package main

import (
    "fmt"
)

func main() {
    test()
}
func test() {
    myMap := make(map[[3]int]bool)
    myMap[[3]int{1, 2, 3}] = true
    myMap[[3]int{0, 5, 4}] = true
    myMap[[3]int{9, 7, 1}] = true
    myMap[[3]int{0, 2, 8}] = true
    array := [][]int{}

    for val := range myMap {
        key := val
        array = append(array, key[:])

    }

    fmt.Println(array)
}

Output:

[[1 2 3] [0 5 4] [9 7 1] [0 2 8]]
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
Gopher
  • 721
  • 3
  • 8