Defer
Go的控制流有一些常用的機制:if, for, switch, goto. 它也擁有可以在獨立的goroutine中運行代碼的go語句。接下來會介紹通常比較少用到的類型:defer, panic, recover.
defer語句將函數(shù)調(diào)用壓入一個列表。當(dāng)包圍defer的函數(shù)返回的時候,列表中緩存的調(diào)用開始執(zhí)行。Defer語句是用來簡化各種各樣的清除操作的。
例如,當(dāng)需要打開兩個文件,將其中一個文件的內(nèi)容拷貝到另外一個文件的時候:
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。如果調(diào)用os.Create失敗,這個函數(shù)就會立即返回卻沒有關(guān)閉源文件。當(dāng)然,可以在出現(xiàn)error的地方加上src.Close方法,但是如果這個函數(shù)非常復(fù)雜,這個問題就會很難被注意到。當(dāng)引入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 語句可以確保我們打開文件之后會關(guān)閉這些文件,不管函數(shù)中有多少個return語句。
Defer 語句的行為非常直接,可以預(yù)測。有三個簡單的規(guī)則:
-
defer語句指定的函數(shù)中的變量是當(dāng)執(zhí)行到defer語句的時候就確定了的;
在下面的例子中,表達(dá)式“i”的值當(dāng)Println被defer的時候就確定了的,所以下面的輸出是0,而不是1func a() { i := 0 defer fmt.Println(i) i++ return } -
多個defer 語句執(zhí)行的順序是后進(jìn)先出的。
下面的函數(shù)打印“3210”func b() { for i := 0; i < 4; i++ { defer fmt.Print(i) } } -
被Defer的函數(shù)可以讀取并且修改外層函數(shù)的被命名的返回值
在下面的例子中,當(dāng)外層函數(shù)返回的時候,defer函數(shù)執(zhí)行,并且將返回值加1,所以這個函數(shù)返回的是2.func c() (i int) { defer func() {i++} () return 1 }