Defer,Panic,Recover簡單應用

Defer

概述
defer語句將函數調用推送到列表上。 周圍函數返回後,將執行已保存的呼叫列表。 Defer通常用於簡化執行各種清理操作的功能。
例子:

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
}

這可行,但是有一個錯誤。 如果對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語句使我們可以考慮在打開每個文件後立即關閉它們,從而確保無論函數中有return語句多少,文件都將被關閉。
關於defer的三個規則:

  1. defer語句執行的參數是執行defer語句時參數的值
func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

運行a()函數得到的輸出是0,因爲defer此語句i的值就是0

  1. 周圍的函數返回後,將按照後進先出的順序執行延遲的函數調用。
func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

輸出3210

  1. defer參數可以讀取並修改函數的返回值
func c() (i int) {
    defer func() { i++ }()
    return 1
}

輸出值爲2

Panic

概述
go語言的內置函數,用於終止正常的控制流程並拋出恐慌。
當函數F調用panic,會執行該函數所有已保存在呼叫隊列的defer函數,然後返回其調用方,對於調用方而言F表現得像是發生了恐慌,該過程將繼續執行對戰,知道返回當前goroutine中的所有函數,此時程序崩潰。可以直接調用panic來生成恐慌,他們也可以是由運行時的錯誤引起的,例如越界數組訪問。

Recover

go語言的內置函數,可以重新活成發生panic的gorouting的控制權。
recover僅僅在defer函數中使用,在正常執行期間,回覆調用將返回nil並沒有其他效果。若goroutine處於panic狀態,改調用會捕獲發送給panic的值並回復goroutine的正常執行。

example

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

輸出:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

若去除掉f中的recover函數

func f() {
	defer func() {
	//	if r := recover(); r != nil {
	//		fmt.Println("Recovered in f", r)
	//	}
	fmt.Println("do nothing")
	}()
	fmt.Println("Calling g.")
	g(0)
	fmt.Println("Returned normally from g.")
}

則並不會輸出Returned normally from f.

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
do nothing
panic: 4

goroutine 1 [running]:
.....

原文:
https://blog.golang.org/defer-panic-and-recover

發佈了211 篇原創文章 · 獲贊 33 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章