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卻有 不同結果發生的問題
參考來源