iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
Software Development

你知道Go是什麼嗎?系列 第 19

Day19 - Defer、Panic、Recover - Golang

  • 分享至 

  • xImage
  •  

在Go語言當中,沒有例外處理機制,CJava中常見的try...catch在Go當中沒有實做出來,而是鼓勵以error的方式去做錯誤檢查,會看到許多套件的方法都是以(target, error)的方式回傳值,而開發時Go也提供了deferpanicrecover去做錯誤處理。

Defer

延遲執行,可將函示延遲至return前一刻才執行,舉個例子的話

package main

import "fmt"

func deferFunc() {
    fmt.Println("有人提到倉鼠嗎?")    
}

func main() {
    defer deferFunc()
    fmt.Println("早安哪")
}

output:

早安哪
有人提到倉鼠嗎?

會發現defer的函式被延遲到最後一刻才執行了

後進先出

defer是將待執行的程式以stack方式儲存,stack屬於後進先出的容器,在使用上要多注意。

package main

import "fmt"

func main() {
	defer fmt.Println("0")
	defer fmt.Println("1")
	defer fmt.Println("2")
	fmt.Println("Hello !")
}

output:

Hello !
2
1
0

使用情境

舉一個讀取檔案的例子,看看下面的例子,若偵測到錯誤的話

func main() {
    f, err := os.Open("/tmp/dat")
    if err != nil {
        fmt.Println(err)
    } else {
        a, err := f.Read()
        // .
        // 正常執行
        // .
        f.Close()
    }
}

看起來沒什麼問題對吧?有openclose,但這支程式有問題的點在於,若正常執行中出現了panic類的錯誤,f.Close()就不會被執行到了。

panic是Go當中的另一種錯誤處理方式,會直接中斷目前的函式執行,並執行defer中的程式,因此若要避免這種狀況發生,就要在資源宣告時配合defer去做資源清除。

func main() {
    f, err := os.Open("/tmp/dat")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer func() { // 延遲執行,而且函式 return 前一定會執行
        if f != nil {
            f.Close()
        }
    }()
    
    a, err := f.Read(b1)
    // .
    // 正常執行
    // .
    
}

這種方式就能確保遇到非預期的錯誤時,資源也能正常關閉

Panic

Go語言沒有拋出例外的機制,如果在執行過程需要中斷目前的流程,就可以使用這種機制。panic會使程式中斷,而且會一路中斷回根節點。

panic時會先執行defer內的東西,執行完後才會繼續回傳panic,並退出程式,因此可以利用這個機制做錯誤處理。

func main() {
	outside()

	fmt.Println("function main")
}
func outside() {
	func() {
		fmt.Println("function inside")
        // panic("test")
	}()
	fmt.Println("function outside")
}

正常執行

output:
function inside
function outside
function main

若將註解去掉,測試panic的話

output:
function inside
發生例外狀況: panic
"test"
.
.

可以看到panic會一路斷回main

Recover

recover是一個函數,可以捕捉panic輸入的值,並且讓程序繼續運行,若成功攔截panic,則函式不會中斷回根結點,就能繼續執行了。

defer內使用recover攔截panic,做完處理後程式就能繼續執行了。

package main

import "fmt"

func panicFunc() {
	fmt.Println("panic function")
	panic("panic")
}

func normalFunc() {
	fmt.Println("normal function")
}

func throwsPanic(fn func()) {
	defer func() {
        x := recover()
		if x != nil {
			fmt.Println("recover happended")
		}
	}()
	fn()
}

func main() {
	throwsPanic(panicFunc)
	throwsPanic(normalFunc)
	fmt.Println("Finish main.")
}

output:

panic function
recover happended
normal function
Finish main.


耶耶,今天的算比較輕鬆的內容,大部分Go方法也都有實做出error的部分,所以要使用哪種方法做錯誤處理主要是看個人習慣或團隊習慣,今天就介紹到這邊吧owo

參考資料

[Golang] 相當好用但又要注意的defer
https://www.evanlin.com/golang-know-using-defer/

GOLANG - 異常處理 - PANIC 與 RECOVER
https://hoohoo.top/blog/golang-exception-handling-panic-and-recover/

defer、panic、recover
https://openhome.cc/Gossip/Go/DeferPanicRecover.html

Go延迟(defer)实例
https://www.yiibai.com/go/golang-defer.html

Go的error與panic
https://www.ithome.com.tw/voice/103455


上一篇
Day18 - Context - Golang
下一篇
Day20 - MySQL - Golang
系列文
你知道Go是什麼嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言