什麼是defer? defer語句是專門在函數結束以後做一些清理工作的。我們先舉一個例子來更好的理解,現在有一個函數,它的作用是把一個文件內容拷貝到另一個文件。 以上代碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那麼就無法執行到文件資源的Close函數。進程每打開一個文件就 ...
什麼是defer?
defer語句是專門在函數結束以後做一些清理工作的。我們先舉一個例子來更好的理解,現在有一個函數,它的作用是把一個文件內容拷貝到另一個文件。
func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
src.Close()
dst.Close()
return
}
以上代碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那麼就無法執行到文件資源的Close函數。進程每打開一個文件就會占用一個文件描述符,而在系統當中,文件描述符是有上限的,可以通過ulimit -n
查看,如果資源沒有被及時釋放,會出現資源浪費的情況。如果打開文件過多,也會出現Too many open files
的提示。這個時候就需要通過defer來解決問題了,代碼如下。
func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
written, err = io.Copy(dst, src)
return
}
defer語句會在return參數設置之後、函數返回給調用者之前執行,這樣就不再擔心文件資源無法被Close了。
defer的三個規則
規則一:被deferred的函數參數在defer時確定
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
我們通過以上代碼來理解這個拗口的規則,如果根據官方的定義來理解這段代碼,變數i的值鐵定為1,但實際執行的結果不是1,卻是0。
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.
那我們再重新來理解這個規則,變數i是在逐行執行到defer語句的時候就已經確定了值,這個時候變數i還沒有進行自增,所以輸出的結果應該是0而不是1。
規則二:被deferred函數執行順序遵循LIFO原則
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
LIFO全稱為Last In First Out
,意為後進先出,棧是一種典型的LIFO數據結構。defer也是如此,拿以上代碼為例,先後遍歷了四次,也就是做了四次壓棧操作。同理,在函數return之前,就會逐一齣棧,倒序執行defer語句,所以上述代碼輸出內容為3210
。
規則三:deferred函數可以讀取和修改函數的返回值
func c() (i int) {
defer func() { i++ }()
return 1
}
我們定義一個defer函數,將變數i自增,那麼最終變數i的值為2。我們從官方文檔中可以看出,defer語句是在函數設置返回值後,且在返回給主調函數前執行的,根據這個思路,c函數已經return了1,這個時候執行了defer函數,將返回的結果1進行了自增,然後返回給主調函數,這個時候主調函數拿到的值就是2了。
deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller