SoFunction
Updated on 2025-03-05

A brief analysis of Range keywords in Go language

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 (indexandvalue), obtain the element in the slice or map respectivelyindexand 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 oneforIn 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…rangeIn the loop.

In Gofor…rangeIn a loop, Go always uses the method of copying the value instead of the traversed element itself. In short, it isfor…rangeThe 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 itvalueThe pointer address of this temporary variable, notlistThe pointer address of an element that is actually traversed to. And throughoutfor…rangeIn the loop,valueThis 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,valueBeing assigned{c}pointer address. Therefore, when list2 outputs, three are displayed&{c}

Similarly, the following is written asfor…rangeThe 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 useindexCome and visitfor…rangeThe 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] = &amp;value
 }

 //A correct example //for i, _ := range list {
 // list2[i] = &amp;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…rangeLoop, 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.