1. 程式人生 > >關於Go關鍵字defer的一些坑

關於Go關鍵字defer的一些坑

    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:
func f() (r int) {
    t := 5
    defer func() {
        t = t + 5
    }()
    return t
}
例3:
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。