Go language range pit

go language use range to traverse slices, map, etc. to give me a lot of convenience.

But when I use range to modify the value, unexpected results may occur.

We will look at the next piece of code:

package main

import (
	"fmt"
)

func main() {
	slice := []int{0, 1, 2, 3}
	myMap := make(map[int]*int)

	for _,v :=range slice{
		if v==1 {
			v=100
		}
	}
	for k,v :=range slice{
		fmt.Println("k:",k,"v:",v)
	}
}

We expect the result to be:

k: 0 v: 0 k: 1 v: 100 k: 2 v: 2 k: 3 v: 3

But actually

k: 0 v: 0 k: 1 v: 1 k: 2 v: 2 k: 3 v: 3

We found that the value of slice did not change

The reason is because the for range traversed content is a copy of the original content, so it cannot be used to modify the contents of the original slice.

The above question, we can use k to directly modify the value according to the index.

	for k,v :=range slice{
		if v==1 {
			slice[k]=100
		}
	}

We look at the pit in the map again

package main

import (
	"fmt"
)

func main() {

	s :=[]int{1,2,3,4}
	m :=make(map[int]*int)

	for k,v:=range s{
		m[k]=&v
	}
	for key, value := range m {
		fmt.Printf("map[%v]=%v\n", key, *value)
	}

	fmt.Println(m)
}

We expect that the printed value should be:

map[0]=1 map[1]=2 map[2]=3 map[3]=4

Actual result:

map[2]=4 map[3]=4 map[0]=4 map[1]=4

From the above results we can guess that range points to The same pointer. With Println we can verify our guess

map[1:0xc00008a000 2:0xc00008a000 3:0xc00008a000 0:0xc00008a000], we can see that our guess is correct

actually still because For range creates a copy of each element, rather than directly returning a reference to each element. If the address of the value variable is used as a pointer to each element, an error will result. In the iteration, the returned variable is a The new variable is assigned according to the slice in the iterative process, so the address of the value is always the same, resulting in the result is not as expected.

Modification is also very simple, we only need to declare an intermediate variable, save the value, and copy it to the map.

package main

import (
	"fmt"
)

func main() {

	s :=[]int{1,2,3,4}
	m :=make(map[int]*int)

	for k,v:=range s{
		n:=v
		m[k]= &n
	}
	for key, value := range m {
		fmt.Printf("map[%v]=%v\n", key, *value)
	}

	fmt.Println(m)
}