Bug
大家看看下面這段程式,思考下輸出結果應該是什麼?
func main() {
n := 0
f := func() func(int, int) {
n = 1
return func(int, int) {}
}
g := func() (int, int) {
println(n)
return 0, 0
}
f()(g())
}
先思考幾秒鐘。。。
在Go 1.13版本之前,上述程式列印的結果是
1
在Go 1.13版本開始,上述程式列印的結果是
0
從習慣認知來看,編譯器應該按照如下順序執行
- step 1: evaluate
f()
,此時變數n
的值為1 - step 2: evaluate
g()
,此時列印n
時,因為step 1已經把n
修改為1了,所以應該列印1 - step3: 根據step 1和step 2的結果,執行最終的函式呼叫
Go編譯器其實也是遵循這個設計的,官方文件的描述如下:
At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations.
Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, and communication operations are evaluated in lexical left-to-right order.
最後一句的表述可以知道,在執行函式呼叫時,對於函式的運算元,語法上是按照從左到右的順序進行解析的。
這個bug其實是Google Go團隊成員Matthew Dempsky提出來的,Go官方認領了這個bug,這個bug產生的原因也很明顯,就是先執行了g()
,然後執行的f()
。
This is because we rewrite
f()(g())
intot1, t2 := g(); f()(t1, t2)
during type checking.gccgo compiles it correctly.
@cuonglm Yeah, rewriting as
t0 := f(); t1, t2 := g(); t0(t1, t2)
instead seems like a reasonable quick fix to me. I think direct function calls and method calls are probably the most common case though, so we should make sure those are still handled efficiently.Longer term, I think we should probably incorporate order.go directly into unified IR.
目前這個bug的影響範圍如下:
- Go 1.13版本開始引入的這個bug,目前已經被修復,預計在1.19版本釋出。
- 只有官方的編譯器
gc
有這個bug,如果你使用的是gccgo
編譯器,也不會有這個問題。 - 只對多個引數的函式呼叫才會有這個bug,比如下面這個例子
f()
的結果是一個函式,該函式只有1個引數,就不會有本文提到的這個bug。
package main
func main() {
n := 0
f := func() func(int) {
n = 1
return func(int) {}
}
g := func() int {
println(n)
return 0
}
f()(g())
}
上面程式執行結果是1
。
推薦閱讀
泛型
Fuzzing
工作區模式
開源地址
文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程。
公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。
個人網站:Jincheng’s Blog。
知乎:無忌