Preface
I believe that friends who have used Range know that the range keyword in Go is very convenient to use. It allows you to traverse a slice or map and pass two parameters (index
andvalue
), obtain the element in the slice or map respectivelyindex
and its value.
For example, use like this:
for index, value := range mySlice { ("index: " + index) ("value: " + value) }
The above example clearly describes the usage of range. In fact, when using the range keyword, there are some special points that need to be paid attention to, and there are some "pits" that are easy for novices to get into.
To illustrate these "pits", we can start with the following slightly more complicated example:
type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) for i, value := range list { list2[i] = &value } (list[0], list[1], list[2]) (list2[0], list2[1], list2[2]) }
In this example, we did some of the following things:
1. Define a structure called Foo, which contains a field called bar. Then we created a slice based on the Foo structure called list
2. We also created a slice based on Foo structure pointer type called list2
3. In onefor
In the loop, we try to iterate through each element in the list, get its pointer address, and assign it to the corresponding position of index in list2.
4. Finally, output each element in list and list2 respectively
From the code, it is natural that the result we expect should be like this:
{A} {B} {C} &{A} &{B} &{C}
But the result was unexpected, the output of the program is as follows:
{A} {B} {C} &{C} &{C} &{C}
Judging from the results, it seems that the three elements in list2 point to the last element in list. Why is this? The problem lies in the above paragraphfor…range
In the loop.
In Gofor…range
In a loop, Go always uses the method of copying the value instead of the traversed element itself. In short, it isfor…range
The one invalue
, is a copy of the value, not the element itself. In this way, when we expect to use & to get the pointer address of the element, we actually just get itvalue
The pointer address of this temporary variable, notlist
The pointer address of an element that is actually traversed to. And throughoutfor…range
In the loop,value
This temporary variable will be reused, so, in the above example, list2 is filled with three identical pointer addresses, and all three addresses point tovalue
, and in the last loop,value
Being assigned{c}
pointer address. Therefore, when list2 outputs, three are displayed&{c}
。
Similarly, the following is written asfor…range
The examples are exactly the same:
var value Foo for var i := 0; i < len(list); i++ { value = list[i] list2[i] = &value }
If we output three elements of list2, the result is also:&{C} &{C} &{C}
So, how is the correct way to write it? We should useindex
Come and visitfor…range
The real element in it and get its pointer address:
for i, _ := range list { list2[i] = &list[i] }
In this way, outputting the element in list2 can get the result we want(&{A} &{B} &{C})
Now.
The experimental code is as follows:
package main import "fmt" type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) //Error example for i, value := range list { list2[i] = &value } //A correct example //for i, _ := range list { // list2[i] = &list[i] //} (list[0], list[1], list[2]) (list2[0], list2[1], list2[2]) }
After understanding the correct usage posture of range, our following example can also be solved:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for _, e := range array { = "foo" } for _, e := range array { () ("--") } }
The most common scenario for writing code is that we need to modify the value of the traversed element in a loop. For example, we hope to use the above examplefor…range
Loop, set the field of each element in the array to "foo" at one time. Similarly, because of the copy of range value, the above program will not output anything...
And the correct way is:
for i, _ := range array { array[i].field = "foo" }
Access each element through index and modify its field, so that a bunch of "foo" can be output...
The experimental code is as follows:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for i, _ := range array { array[i].field = "foo" } for _, e := range array { () } }
Summarize
The above is all about the Range keywords in Go. This article is still very detailed. I believe that this article will have certain reference value for everyone to learn Go. If you have any questions, you can leave a message to communicate. The editor will reply to you as soon as possible. Please continue to support me.