0
func TestMapValuePointer2(t *testing.T) {
    fmt.Println("Test Map Value Pointer 2")
    m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
    n := len(m)
    array := make([]*int, n)
    i := 0
    for _, v := range m {
        array[i] = &v
        fmt.Printf("Add to array: %d\n", v)
        i++
    }
    for _, k := range array {
        fmt.Printf("Value: %d\n", *k)
    }
}

The output is not:

Value: 912
Value: 3711
Value: 2138
Value: 1908

Instead, the output maybe like:

Value: 912
Value: 912
Value: 912
Value: 912

Can someone explain why the results are like the above?

t j
  • 7,026
  • 12
  • 46
  • 66
Henry0100
  • 19
  • 2
  • 3
    You keep adding the same pointer to `v` to the slice. See also https://golang.org/doc/faq#closures_and_goroutines – JimB Nov 21 '16 at 22:45
  • If you want pointers you need to make your map's value a pointer type like `*int`. Map items can move around in memory, so you can't get a pointer directly into the map. [Here's a slightly expanded version of that.](http://stackoverflow.com/a/32751792/2714852) – twotwotwo Nov 21 '16 at 23:44

2 Answers2

2

Quoting from this doc:

[...] each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable [...]

In other words, the loop variable v is reused in all iterations, so your assigning the same pointer to all your slice elements.

Variables declared inside the loop won't be recycled like that, so this will work as you would expect:

for _, v := range m {
    vv := v
    array[i] = &vv
    fmt.Printf("Add to array: %d\n", v)
    i++
}

Btw, you haven't explained why you want to use *int as the type of the values. All this would be simpler if you used simply int values.

janos
  • 120,954
  • 29
  • 226
  • 236
  • I am not sure what's OP expected value here, but doing it this way the pointers inside the array and map will now deference to different places. – Ashwini Chaudhary Nov 21 '16 at 23:26
1

The issue here is that the variable v created during the loop is actually a copy of the value we have in the map. The memory related to v is allocated once at the start of the loop and is re-used later on for other values, as the last value here points to is 912 in this case you're seeing 912 inside the array.

A simple proof for this will be: https://play.golang.org/p/K0yAbEIf3G

One fix for this will be to change your map value to be *int pointers instead of values and later on we can dereference them to get the actual values:

package main

import "fmt"

func main() {
    fmt.Println("Test Map Value Pointer 2")
    a, b, c, d := 3711, 2138, 1908, 912
    m := map[string]*int{"rsc": &a, "r": &b, "gri": &c, "adg": &d}
    n := len(m)
    array := make([]*int, n)
    i := 0
    for _, v := range m {
        array[i] = v
        fmt.Printf("Add to array: %d\n", *v)
        i++
    }
    for _, k := range array {
        fmt.Printf("Value: %d\n", *k)
    }
}

Playground link: https://play.golang.org/p/TpkXlCElLx

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504