golang 個人筆記和心得

defer panic recover

defer

先看以下範例

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.

參考網站

panic recover

go 沒有 try catch , 原因是 go 的開發者認為, 當過多 try catch 時會造成程式碼很難閱讀. 所以提供了 panic recover 優雅的解決這個問題.