新學習go語言的人可能遇到常見的錯誤,其中有兩個比較常見的錯誤,須要單獨拿出來講下,爲何要單獨說呢,由於這兩個錯誤跟其餘語言不一樣,是由於go自己的設計形成的。git
在go語言中,循環(迭代)所使用的變量是同一個變量,只是在每次循環的時候被賦於不一樣的值,這樣的作的目的呢,固然是出於高效考慮咯。可是,若是使用不當的話,可能會引發意想不到的行爲。程序員
舉一個栗子:github
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])
}
複製代碼
上面的代碼會輸出:golang
Values: 3 3 3
Addresses: 0x40e020 0x40e020 0x40e020
複製代碼
由於每次循環中,咱們只是把變量 i
的地址放進 out
數組裏,由於變量 i
是同一個變量,只有在循環結束的時候,被賦值爲3。數組
解決方法:申明一個新的變量bash
for i := 0; i < 3; i++ {
i := i // Copy i into a new variable.
out = append(out, &i)
}
複製代碼
結果併發
Values: 0 1 2
Addresses: 0x40e020 0x40e024 0x40e028
複製代碼
同理對於切片來講,也用有這個問題,由於切片自己就只是一個地址而已app
func main() {
var out [][]int
for _, i := range [][1]int{{1}, {2}, {3}} {
out = append(out, i[:])
}
fmt.Println("Values:", out)
}
複製代碼
結果:oop
Values: [[3] [3] [3]]
複製代碼
一樣的問題,在循環裏使用協程也會遇到學習
按照程序員的思惟,都喜歡使用併發,你可能會寫出下面的代碼: 內心特別開心,原來go 的併發這麼簡單。
for _, val := range values {
go func() {
fmt.Println(val)
}()
}
複製代碼
可是,你可能會發現輸出的結果是一摸同樣的! 由於go的協程跑起來也是須要一點時間的,循環結束的時候,可能一個goroute都沒有跑完,而後 val
值確被賦值了,因此,你會看到,輸出的都是最後一個值
解決方法:
for _, val := range values {
go func(val interface{}) {
fmt.Println(val)
}(val)
}
複製代碼
固然也能夠
for i := range valslice {
val := valslice[i]
go func() {
fmt.Println(val)
}()
}
複製代碼
Reference:github.com/golang/go/w…