引言
Go有許多在其他編程語言中可以找到的常見控制流關鍵字,例如if
、switch
、for
等。defer
是其他大多數編程語言中沒有的關鍵字,盡管它不太常見,但你很快就會看到它在你的程序中有多么有用。
defer
語句的主要用途之一是清理資源,例如打開的文件,網絡連接和數據庫句柄。在程序使用完這些資源后,關閉它們是很重要的,以避免耗盡程序的限制,并允許其他程序訪問這些資源。通過將關閉文件/資源的調用保持在接近open調用的位置,defer
使我們的代碼更清晰,更不容易出錯。
在本文中,我們將學習如何正確使用defer
語句來清理資源,以及在使用defer
時所犯的幾個常見錯誤。
什么是defer
語句
defer
語句將defer
關鍵字后面的[function]調用添加到棧上。當添加它們的函數返回時,該堆棧上的所有調用都會被調用。因為這些調用是放在棧上的,所以它們是按照后進先出的順序調用的。
讓我們通過打印一些文本來看看defer
是如何工作的:
main.go
package mainimport "fmt"func main() {defer fmt.Println("Bye")fmt.Println("Hi")
}
在main
函數中,我們有兩個語句。第一個語句以defer
關鍵字開始,接著是print
語句,打印出Bye
。下一行打印出Hi
。
如果運行這個程序,會看到如下輸出:
OutputHi
Bye
注意,首先打印的是Hi
。這是因為任何以defer
關鍵字開頭的語句直到使用defer
的函數結束時才會被調用。
再看一下這個程序,這次我們將添加一些注釋來幫助解釋所發生的事情:
main.go
package mainimport "fmt"func main() {// defer statement is executed, and places// fmt.Println("Bye") on a list to be executed prior to the function returningdefer fmt.Println("Bye")// The next line is executed immediatelyfmt.Println("Hi")// fmt.Println*("Bye") is now invoked, as we are at the end of the function scope
}
理解defer
的關鍵是,當defer
語句執行時,延遲函數的參數會立即計算。當defer
執行時,它將后面的語句放在一個列表中,以便在函數返回之前調用。
盡管這段代碼說明了defer
的運行順序,但這并不是編寫Go程序時使用它的典型方式。我們更有可能使用defer
來清理資源,例如文件句柄。接下來讓我們看看如何做到這一點。
使用defer
來清理資源
在Go中使用defer
來清理資源是很常見的。讓我們首先來看一個程序,它將字符串寫入文件,但不使用defer
來處理資源清理:
main.go
package mainimport ("io""log""os"
)func main() {if err := write("readme.txt", "This is a readme file"); err != nil {log.Fatal("failed to write file:", err)}
}func write(fileName string, text string) error {file, err := os.Create(fileName