golang - 異常處理

侯佳奇發表於2024-07-02

基本的異常處理

目前流行的語言錯誤處理都是透過 try catch finally 關鍵字或類關鍵字處理的

但是在 golang 中提供了一些全新模式, 不同於 try catch 語句, go語言透過函式返回錯誤來捕獲, go內建了error介面, 只要實現Error() string方法, 透過判斷 err 是否是 nil 來捕獲異常.

func div(x float32, y float32) (result float32, err error) {}

golang 中提供了 defer 關鍵字, 這個語句會在執行完當前函式後執行, 類似 finally

func main() {
    // 相當於finally
    defer println("done!")
	println("hello, world")
}

舉個具體的例子:

package main

import (
	"fmt"
	"math"
)

// 宣告零除錯誤
type ZeroDivError struct {
	code    int
	message string
}

// 實現error介面
func (e ZeroDivError) Error() string {
	return fmt.Sprintf("%v: %v", e.code, e.message)
}

func div(x float32, y float32) (result float32, err error) {
	if y == 0 {
		return float32(math.Inf(1)), ZeroDivError{1, "零除問題"}
	}
	return x / y, err
}

func main() {
    // 相當於finally
    defer func() {
        println("done!")
    }

	// 這個相當於
	// try { r := div(1, 0) } catch e { println(r, e.Error() }
	r, e := div(1, 0)
	if e != nil {
		println(r, e.Error())
	}
}

panic 和 recover

golang 還提供了兩個關鍵字 panicrecover, 按照上面的異常處理方式需要把錯誤一層一層的傳遞出去, 這兩個關鍵字主要目的是用來做跨層級的異常捕獲.

panic 用來立馬中斷當前程式返回異常, recover 用來捕獲 panic 異常程式會繼續執行

recover 只能在 defer 中才可以生效

panic 可以多次執行, 呼叫 recover 時會按順序捕獲錯誤

package main

import "fmt"

func a() {
	defer func() {
		e := recover()
		fmt.Printf("a e: %v\n", e)
		println("a done!")
	}()
	panic("a fatal!")
}

func b() {
	defer func() {
		e := recover()
		fmt.Printf("b e: %v\n", e)
		println("b done!")
	}()
	panic("b fatal!")
}

func main() {
	defer func() {
		e := recover()
		fmt.Printf("main e: %v\n", e)
		println("main done!")
	}()
	a()
	b()
	panic("main fatal!")
}

執行結果

a e: a fatal!
a done!
b e: b fatal!
b done!
main e: main fatal!
main done!