Go 拋棄了傳統的 try/catch 異常機制,採用多返回值約定:最後一個返回值為 error 表示執行狀態。本文深入解析 error 接口本質、構造方法、自定義錯誤類型,並展示如何將重複的 if err != nil 重構為優雅的鏈式檢查模式。
type error interface {
Error() string
}
任何實現了 Error() string 方法的類型均滿足 error 接口。
var dividedErr = errors.New("Cant divided by 0")
err := fmt.Errorf("%d cant divided by 0", arg)
type division struct {
arg int
str string
}
func (e *division) Error() string {
return fmt.Sprintf("%d %s", e.arg, e.str)
}
func divideCheck(arg1, arg2 int) error {
if arg2 == 0 {
return &division{arg1, "can't divided by 0"}
}
return nil
}
func main() {
var err error
err = divideCheck(4, 2)
if err != nil { fmt.Println(err); return }
err = divideCheck(8, 0)
if err != nil { fmt.Println(err); return }
}
每次調用需 3 行處理,代碼嘈雜。
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()) // 統一處理
}
}
優勢:
if err != nilpkg/errors 或 Go 1.13+ fmt.Errorf("%w", err) 支援錯誤包裝與堆疊追蹤。var ErrNotFound = errors.New("not found")) 配合 errors.Is() 判斷特定錯誤。Unwrap() 支援錯誤鏈遍歷。Go 的錯誤處理常被詬病,但掌握「以 Go 的方式編程」—— 值語義、組合優於繼承、顯式錯誤流 —— 依然能寫出清晰、健壯的代碼。