SoFunction
Updated on 2025-03-04

Introduction to address fetching operation traps of for range in golang

Tips: for range creates a copy of each element instead of directly returning a reference to each element

Example 1:

package main
import "fmt"
func main() {
 slice := []int{0, 1, 2, 3}
 myMap := make(map[int]*int)
 for index, value := range slice {
  myMap[index] = &value
 }
 ("=====new map=====")
 prtMap(myMap)
}
 
func prtMap(myMap map[int]*int) {
 for key, value := range myMap {
  ("map[%v]=%v\n", key, *value)
 }
}

Output:

dotzdeMacBook-Pro-2:src dotz$ ./range

=====new map=====

map[0]=3

map[1]=3

map[2]=3

map[3]=3

Example 2:

package main  
import "fmt"  
type Test struct {
    name string
}
 
func (this *Test) Point() { // this is a pointer    ()
}
  
func main() {  
    ts := []Test{{"a"}, {"b"}, {"c"}}
    for _, t := range ts {
        defer () //Output c c c c    } 
} 

Output:

dotzdeMacBook-Pro-2:src dotz$ ./method

c

c

c

Example 1 We expect outputs 0, 1, 2, 3, Example 2 We expect outputs a, b, c, but the outputs of both examples are not what we expected.

For Example 1, it is obvious that the address is fetched, and the address of the value variable is taken every time. Therefore, the values ​​of all elements in the map are the addresses of the value variable (references), because the last value is assigned to 3, and all outputs are 3.

For Example 2, it is a bit obscure, which is mixed with the rules of defer and method receiver, but in fact, it is the same as Example 1. When executing(), the address (reference) of t is obtained. At the end of for, t is assigned to the address of "c". When the main function returns, the receiving method Point of "c" is executed, so the output is "c".

Supplement: golang address fetching operation pit: for idx,item := range arr The item in the item is an independent object

Let's look at the code first:

package main
import "fmt"
func main() {
    type s struct {
        A string
        B int32
    }
    arr := []s{
        {"123", 123},
        {"456", 456},
        {"789", 789},
    }
    m := make(map[string]*s)
    for idx, item := range arr {
        m[] = &item
        ("idx=%d, addr=%p, item addr=%p\n", idx, &arr[idx], &item)
    }
    for k, v := range m {
        ("key=%s, v=%+v\n", k, v)
    }
}

Run output:

idx=0, addr=0xc00004e050, item addr=0xc0000044a0

idx=1, addr=0xc00004e068, item addr=0xc0000044a0

idx=2, addr=0xc00004e080, item addr=0xc0000044a0

key=123, v=&{A:789 B:789}

key=456, v=&{A:789 B:789}

key=789, v=&{A:789 B:789}

I foolishly took the address of the item in the loop, and as a result, all the values ​​in the map point to the last one!

It seems that item is an independent object, which points to the corresponding element in the array.

The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.