1. 程式人生 > >Defer, Panic, and Recover

Defer, Panic, and Recover

4 August 2010

Go has the usual mechanisms for control flow: if, for, switch, goto. It also has the go statement to run code in a separate goroutine. Here I'd like to discuss some of the less common ones: defer, panic, and recover.

A defer statement

pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

For example, let's look at a function that opens two files and copies the contents of one file to the other:

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
}

This works, but there is a bug. If the call to os.Create fails, the function will return without closing the source file. This can be easily remedied by putting a call to src.Close before the second return statement, but if the function were more complex the problem might not be so easily noticed and resolved. By introducing defer statements we can ensure that the files are always closed:

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 statements allow us to think about closing each file right after opening it, guaranteeing that, regardless of the number of return statements in the function, the files will be closed.

The behavior of defer statements is straightforward and predictable. There are three simple rules:

1. A deferred function's arguments are evaluated when the defer statement is evaluated.

In this example, the expression "i" is evaluated when the Println call is deferred. The deferred call will print "0" after the function returns.

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

2. Deferred function calls are executed in Last In First Out order after the surrounding function returns.

This function prints "3210":

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

3. Deferred functions may read and assign to the returning function's named return values.

In this example, a deferred function increments the return value i after the surrounding function returns. Thus, this function returns 2:

func c() (i int) {
    defer func() { i++ }()
    return 1
}

This is convenient for modifying the error return value of a function; we will see an example of this shortly.

Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.

Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

Here's an example program that demonstrates the mechanics of panic and defer:

package main

import "fmt"

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)
}

The function g takes the int i, and panics if i is greater than 3, or else it calls itself with the argument i+1. The function f defers a function that calls recover and prints the recovered value (if it is non-nil). Try to picture what the output of this program might be before reading on.

The program will output:

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.

If we remove the deferred function from f the panic is not recovered and reaches the top of the goroutine's call stack, terminating the program. This modified program will output:

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
panic: 4
 
panic PC=0x2a9cd8
[stack trace omitted]

For a real-world example of panic and recover, see the json package from the Go standard library. It decodes JSON-encoded data with a set of recursive functions. When malformed JSON is encountered, the parser calls panic to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'unmarshal' methods of the decodeState type in decode.go).

The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.

Other uses of defer (beyond the file.Close example given earlier) include releasing a mutex:

mu.Lock()
defer mu.Unlock()

printing a footer:

printHeader()
defer printFooter()

and more.

In summary, the defer statement (with or without panic and recover) provides an unusual and powerful mechanism for control flow. It can be used to model a number of features implemented by special-purpose structures in other programming languages. Try it out.

相關推薦

15 Defer, Panic, and Recover

-m sed targe cto evaluate aws more unmarshal got Defer, Panic, and Recover 4 August 2010 Go has the usual mechanisms for control flow: i

Defer, Panic, and Recover

4 August 2010 Go has the usual mechanisms for control flow: if, for, switch, goto. It also has the go state

golang教程之Panic and Recover

文章目錄 Panic and Recover 什麼是panic? 何時應該使用`panic`? Panic的例子 延遲panic Recover panic,recover和Goroutines 執行時

Golang Panic and Recover

What is panic? The idiomatic way to handle abnormal conditions in a program in Go is using errors. Errors are sufficient for most of the ab

Golang 入門系列(十四)defer, panicrecover用法

以前講過golang 的基本語法。但是,只是講了一些基礎的語法,感興趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,前段時間有人問我defer,recover的用法。所以,還是統一的總結一下相關的關鍵字

GO_05_2:Golang 中 panicrecoverdefer 的用法

log logs lan finall 可能 錯誤處理 錯誤 異常處理 auto 函數 defer   1. 它的執行方式類似其他語言中的折構函數,在函數體執行結束後按照調用順序的 相反順序 逐個執行   2. 即使函數發生 嚴重錯誤 也會被執行,類似於 java 中 t

Go基礎系列:deferpanicrecover

pre 也會 副本 r語 tro 釋放資源 goroutine rec learn defer關鍵字 defer關鍵字可以讓函數或語句延遲到函數語句塊的最結尾時,即即將退出函數時執行,即便函數中途報錯結束、即便已經panic()、即便函數已經return了,也都會執行def

關於go語言中的defer\panic\recover

defer的作用 類似其他語言的finally{},可以非常方便地在獲得資源之後直接defer釋放資源,保證資源釋放,或者其他的一些處理工作 defer的執行時機 在函式體執行完成之後,回到呼叫點之前。這意味這你可以在defer中修改return的值 函式中出現了錯誤(panic)d

defer panic recover

defer 1.詞義     defer推遲  recover恢復  panic恐慌 2.panic    panic 型別interface{}型別    panic恐慌 不會再往下執行了

Go 學習之路: 異常處理deferpanicrecover

Go沒有像Java那樣的異常機制,它不能丟擲異常。因為設計者們認為,將異常與控制結構混在一起容易使得程式碼變得混亂。於是乎引入Exception處理: defer,panic,recover; 簡單描述:Go中可以丟擲一個panic的異常,然後在defe

go語言中使用deferpanicrecover處理異常

baidu 繼續 spa hello http tid 處理流 dex integer go語言中的異常處理,沒有try...catch等,而是使用defer、panic、recover來處理異常。 1、首先,panic 是用來表示非常嚴重的不可恢復的錯誤的。在Go語

Go語言異常處理defer\panic\recover

Go語言異常處理defer\panic\recover Go語言追求簡潔優雅,所以,Go語言不支援傳統的 try…catch…f

Golang 高效實踐之deferpanicrecover實踐

 前言 我們知道Golang處理異常是用error返回的方式,然後呼叫方根據error的值走不同的處理邏輯。但是,如果程式觸發其他的嚴重異常,比如說陣列越界,程式就要直接崩潰。Golang有沒有一種異常捕獲和恢復機制呢?這個就是本文要講的panic和recover。其中recover要配合defe

Go語言聖經-Panic異常,Recover捕獲異常習題

匿名函數 AC 輸出 func 導致 繼續 res 發生 pre Go語言聖經-Panic異常1.當panic異常發生時,程序會中斷運行,並立即執行在該goroutine中被延遲的函數(defer 機制)2.不是所有的panic異常都來自運行時,直接調用內置的panic函數

Golang中的panicrecover

func panic(interface{})和func recover() interface{}是Golang中用於錯誤處理的兩個函式。 panic的作用就是丟擲一條錯誤資訊,從它的引數型別可以看到它可以丟擲任意型別的錯誤資訊。在函式執行過程中的某處呼叫了panic,則立即丟擲一個錯誤資訊,同時

Golang 中的 panicrecover 理解

Golang 有2個內建的函式 panic() 和 recover(),用以報告和捕獲執行時發生的程式錯誤,與 error 不同,panic-recover 一般用在函式內部。一定要注意不要濫用 panic-recover,可能會導致效能問題。 使用panic

Go基礎程式設計:異常處理(error介面、panicrecover)

1 error介面 Go語言引入了一個關於錯誤處理的標準模式,即error介面,它是Go語言內建的介面型別,該介面的定義如下: type error interface { Error() string } Go語言的標準庫程式碼包errors

go panicrecover分析及錯誤處理

先介紹一下什麼叫error 型別 error 是一種型別,表示錯誤狀態的型別,如果沒有錯誤則是nil。直白點將:error 型別就是描述錯誤的一種型別。 panic 在golang goroutine 的作用 panic 官方文件介紹:

Go學習筆記:panic()和recover()

如何理解: 1、呼叫panic函式相當於throw一個異常,逐層啟動異常處理流程。在呼叫panic之前defer的操作會在呼叫panic後立即執行。 2、呼叫recover函式相當於catch了異常,會中止異常處理流程,並可以返回這個異常。 3、panic函式接受一個引數,

Brocade SAN Switch CFG backup and recover

通常,我們在執行SAN交換機變更之前,需要先對SAN交換機的配置做一次備份,尤其是在刪除ZONE配置,或者一些其他重大變更。 那麼本文主要探討如何來做SAN交換機的備份和恢復。 1. 備份 switch:admin> configupload Protocol (s