for-loopgoreference

Copying the address of a loop variable in Go


In the following code sample, the result is not what I would expect:

package main

import "fmt"

func main() {
    src := map[int]int{1: 1, 2: 2, 3: 3}
    fmt.Println("src ", src)

    dst := make([]*int, 0, len(src))
    for k, _ := range src {
        dst = append(dst, &k)
    }
    for _, a := range dst {
        fmt.Print(*a, " ")
    }
    fmt.Println()
}

Result:

src map[1:1 2:2 3:3]
3 3 3

Go Playground: https://play.golang.org/p/BSDsd3nojz

but I understand what is happening. The unchanging address of k is being added to dst, so when I loop over dst, the same value is in every location: 3.

The address of k never changes in the loop, so the second loop keeps referring to that location, containing the last value it had, 3.

How can I get the address of the current value of k to be copied? Do I need something like this:

for k, _ := range src {
    key = new(int)
    *key = k
    dst = append(dst, key)
}

That seems awkward.


Solution

  • If you have a map[T]X and you want to get a []*T, you are on the right track with copying the loop variable and getting an address to it.

    There is a slightly slimmer way to do it than your way:

    for k := range src {
        key := k
        dst = append(dst, &key)
    }
    

    What you are adding to dst is not the address of the keys or values in the map, but rather the address of a copy of the key. That distinction may or may not matter to you.

    The reason that using the address of the loop variables doesn't work, is that the loop variables are single locations that get updated on each iteration. Value types like ints and structs are copyed each time you assign it to a new variable.