defer
語句將函數調用壓到棧中,函數調用的順序遵循Last In First Out
的原則。棧中的函數將在調用函數返回之後被執行。defer
通常用在簡單的函數來執行清理工作。
Example:
func CopyFile(dstName, 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)
dst.Close()
src.Close()
return
}
上述的代碼中存在一個bug,如果在os.Create()
函數執行失敗後,打開的文件將不能關閉,所以補救的措施就是在os.Create()
函數創建失敗return
的時候,執行src.Close()
語句。但是問題來了,如果函數很複雜以至於問題不能被輕易的發現怎麼辦,所以這裡使用defer
語句就可以方便解決這個問題。
改寫後的代碼如下:
func CopyFile(dstName, 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()
return io.Copy(dst, src)
}
defer
語句的行為是簡單直觀,並且可預測。關於defer
有下面三條簡單的規則:
- 當
defer
語句給定的時候,defer
函數的參數也是給定的
// 該函數打印的值為0
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
-
defer
函數調用在調用函數返回之後按照LIFO的順序被執行
// 該函數將打印"3210"
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
-
defer
函數可能對函數返回值進行重新讀取和賦值
// 該函數的返回值為2
func c() (i int) {
defer func() { i++ }()
return 1
}
這對於修改函數的錯誤返回值是很方便的。