mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
Common mistakes: Add "using reference to loop iterator variable"
parent
ca3708fee9
commit
e6ad5632aa
@ -2,12 +2,72 @@ Table of Contents
|
||||
=================
|
||||
|
||||
+ [Introduction](#introduction)
|
||||
+ [Using reference to loop iterator variable](#using-reference-to-loop-iterator-variable)
|
||||
+ [Using goroutines on loop iterator variables](#using-goroutines-on-loop-iterator-variables)
|
||||
|
||||
# Introduction
|
||||
|
||||
When new programmers start using Go or when old Go programmers start using a new concept, there are some common mistakes that many of them make. Here is a non-exhaustive list of some frequent mistakes that show up on the mailing lists and in IRC.
|
||||
|
||||
# Using reference to loop iterator variable
|
||||
|
||||
In Go, the loop iterator variable is a single variable that takes different values in each loop iteration. This is very efficient, but might lead to unintended behavior when used incorrectly. For example, see the following program:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var out []*int
|
||||
for i := 0; i < 3; i++ {
|
||||
out = append(out, &i)
|
||||
}
|
||||
fmt.Println("Values:", *out[0], *out[1], *out[2])
|
||||
fmt.Println("Addresses:", out[0], out[1], out[2])
|
||||
}
|
||||
```
|
||||
|
||||
It will output unexpected results:
|
||||
|
||||
```
|
||||
Values: 3 3 3
|
||||
Addresses: 0x40e020 0x40e020 0x40e020
|
||||
```
|
||||
|
||||
Explanation: in each iteration we append the address of `i` to the `out` slice, but since it is the same variable, we append the same address which eventually contains the last value that was assigned to `i`. One of the solutions is to copy the loop variable into a new variable:
|
||||
|
||||
```diff
|
||||
for i := 0; i < 3; i++ {
|
||||
+ i := i // Copy i into a new variable.
|
||||
out = append(out, &i)
|
||||
}
|
||||
```
|
||||
|
||||
The new output of the program is what was expected:
|
||||
|
||||
```
|
||||
Values: 0 1 2
|
||||
Addresses: 0x40e020 0x40e024 0x40e028
|
||||
```
|
||||
|
||||
Explanation: the line `i := i` copies the loop variable `i` into a new variable scoped to the for loop body block, also called `i`. The address of the new variable is the one that is appended to the array, which makes it outlive the for loop body block. In each loop iteration a new variable is created.
|
||||
|
||||
While this example might look a bit obvious, the same unexpected behavior could be more hidden in some other cases. For example, the loop variable can be an array and the reference can be a slice:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var out [][]int
|
||||
for _, i := range [][1]int{{1}, {2}, {3}} {
|
||||
out = append(out, i[:])
|
||||
}
|
||||
fmt.Println("Values:", out)
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Values: [[3] [3] [3]]
|
||||
```
|
||||
|
||||
The same issue can be demonstrated also when the loop variable is being used in a Goroutine (see the following section).
|
||||
|
||||
# Using goroutines on loop iterator variables
|
||||
|
||||
When iterating in Go, one might attempt to use goroutines to process data in parallel. For example, you might write something like this, using a closure:
|
||||
|
Loading…
x
Reference in New Issue
Block a user