Skip to main content

Golang for range 無法正常取值

在golang1.21(包含1.21)之前,下面這組code會出現很微妙的狀況

package main

import "fmt"

func main() {
nums := []int{1, 2, 3, 4, 5}
var first *int
for _, num := range nums {
if first == nil {
first = &num
}
fmt.Println(*first)
}
}

1.21之前的輸出為 1 2 3 4 5

1.22的輸出為 1 1 1 1 1

在1.21前,for的range的語法糖如果解開

for i, num := range nums {

}

會變成下列的狀況

{
i := 0
num := 0
for ; i < len(nums); i++ {
num = nums[i]
// do something...
}
}

語法糖中的 i 跟 num 是事先宣告一個全域變數,在開始進行運算的 所以在一開始的描述中

package main

import "fmt"

func main() {
nums := []int{1, 2, 3, 4, 5}
var first *int
for _, num := range nums {
if first == nil {
first = &num
}
fmt.Println(*first)
}
}

這個first才會一直變動,因為first的point被指到num這個全域變數上了,所以才會輸出

1
2
3
4
5

如果要解決,通常會重複在宣告一次,讓變數存在於for loop中

package main

import "fmt"

func main() {
nums := []int{1, 2, 3, 4, 5}
var first *int
for _, num := range nums {
num := num
if first == nil {
first = &num
}
fmt.Println(*first)
}
}

抑或是直接透過num[i]來取值

package main

import "fmt"

func main() {
nums := []int{1, 2, 3, 4, 5}
var first *int
for i := range nums {
if first == nil {
first = &nums[i]
}
fmt.Println(*first)
}
}

而在go 1.21版時,開發團隊將這個問題做了一個實驗性的修正

https://go.dev/blog/loopvar-preview

讓每次的for range都是新的變數,就可以避免到上面這個問題發生,然後這個功能也在1.22版中納入正式的開發環境

https://tip.golang.org/doc/go1.22

所以才會出現最前面提到的,同樣的code卻有不同結果發生的問題

參考來源