關於Go關鍵字defer的一些坑
阿新 • • 發佈:2019-02-09
defer意為延遲,在Go語言中用於延遲執行一個函式。它可以幫助我們處理容易忽略的問題,如資源釋放、連線關閉等。但在時間使用過程中,有一些需要注意的地方(坑)。
結論
1.若函式中有多個defer,其執行順序為先進後出,可以理解為棧。
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
defer fmt.Println(i)
}
}
Output:
4
3
2
1
0
2.return會做幾件事
1.給返回值賦值;
2.呼叫defer表示式;
3.返回呼叫函式;
package main import "fmt" func main() { fmt.Println(increase(1)) } func increase(d int) (ret int) { defer func() { ret++ }() return d } Output: 2
2.若defer表示式有返回值,將會被丟棄。
閉包和匿名函式
匿名函式:沒有函式名的函式。
閉包:可以使用另外一個函式作用域中的變數的函式。
在實際開發中,defer的使用經常伴隨著閉包和匿名函式的使用。小心踩坑:
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
defer func() {
fmt.Println(i)
}()
}
}
Output:
5
5
5
5
5
解釋一下,defer表示式中的i是對for迴圈中i的引用。到最後,i加到5,故而最後全部列印5。如果將i作為引數傳入defer表示式,在傳入最初就會進行求值儲存,只是沒有執行延遲函式而已。
for i := 0; i < 5; i++ {
defer func(idx int) {
fmt.Println(idx)
}(i) // 傳入的 i,會立即被求值儲存為 idx
}
鞏固一下
為了鞏固一下上面的知識點,我們來思考幾個例子。
例1:
func f() (result int) {
defer func() {
result++
}()
return 0
}
例2:
例3:func f() (r int) { t := 5 defer func() { t = t + 5 }() return t }
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
分析如下:
例1,比較簡單,參考結論2,將0賦值為result,defer延遲函式修改result,最後返回給呼叫函式,正確答案是1。
例2,defer是在t賦值給r之後執行的,而defer延遲函式只是改變了t的值,r不變,正確答案是5。
例3,這裡將r作為引數傳入了defer表示式。故而func(r int)中的r非func f()(r int)中的r,只是引數命名相同而已。正確答案是1。