先看以下範例
func f(){
for i := 0 ; i < 5 ; i++{
fmt.Println(i)
}
fmt.Println("f finish")
}
此時的結果是
0
1
2
3
4
f finish
如果再 fmt.Println 前面加上 defer
func f(){
for i := 0 ; i < 5 ; i++{
defer fmt.Println(i)
}
fmt.Println("f finish")
}
結果就變成
f finish
4
3
2
1
0
defer 是讓 fmt.Println(0) , fmt.Println(1) , fmt.Println(2) , fmt.Println(3) , fmt.Println(4) 依序放到清單中, 等到 func f 結束前, 再依據 Last-In-First-Out (LIFO) 的順序 call 清單中的 function.
這樣功能提供了什麼的好處?
我們再看以下的例子
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(dstName) 失敗的時候 function 就會 return, 而 src.Close() 就沒有執行到了.
所以可以改成這樣
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 {
src.Close()
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
但是如果開多個檔案? 那會很麻煩
所以我們利用 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)
}
這時不管如何, 都會執行 close function.
go 沒有 try catch , 原因是 go 的開發者認為, 當過多 try catch 時會造成程式碼很難閱讀. 所以提供了 panic recover 優雅的解決這個問題.