Go 沒有傳統的 try/catch 異常機制,而是透過函數回傳值的最後一個參數回傳 error 介面來表示執行狀態。本文深入解析 error 介面本質、自訂錯誤類型、優雅的錯誤處理模式與實戰優化技巧。
n, err := someFunc()
if err != nil {
// 處理錯誤
}
或簡寫為:
if n, err := someFunc(); err != nil {
// 處理錯誤
}
errors.New("錯誤訊息"):建立靜態錯誤fmt.Errorf("格式化字串", 參數...):建立動態格式化錯誤var dividedErr = errors.New("Cannot divide by zero")
// 或
err := fmt.Errorf("%d cannot divide by zero", arg)
error 在標準庫中定義為單一方法的介面:
type error interface {
Error() string
}
任何實作 Error() string 方法的型別即滿足 error 介面。我們可定義結構體攜帶豐富上下文:
type division struct {
arg int
msg string
}
func (e *division) Error() string {
return fmt.Sprintf("%d %s", e.arg, e.msg)
}
func divideCheck(arg1, arg2 int) error {
if arg2 == 0 {
return &division{arg1, "can't divided by 0"}
}
return nil
}
連續呼叫函數若每次都檢查錯誤會顯得冗長。可封裝一個持有錯誤狀態的結構體,僅在最後統一檢查:
type division struct {
err error
}
func (d *division) DivideCheck(arg1, arg2 int) {
if d.err != nil { return } // 已有錯誤則短路
if arg2 == 0 {
d.err = fmt.Errorf("%d can't divided by 0", arg1)
}
}
func (d *division) Err() error { return d.err }
func main() {
d := new(division)
d.DivideCheck(4, 2)
d.DivideCheck(8, 0)
if d.Err() != nil {
fmt.Println(d.Err())
}
}
此模式優點:
Go 的錯誤處理常被詬病冗長,但採用「以 Go 的方式思考」——利用介面多型、狀態累積、短路檢查——即可寫出優雅、高效且可維護的錯誤處理邏輯。