错误处理
Go 没有异常机制(try-catch),错误通过返回值 error 显式传递,强调”错误也是值”。
error 接口
1 2 3
| type error interface { Error() string }
|
任何实现了 Error() string 的类型都是 error。
基本用法
1 2 3 4 5 6 7 8 9 10 11 12
| func readConfig(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config: %w", err) } return data, nil }
data, err := readConfig("app.yaml") if err != nil { log.Fatal(err) }
|
创建错误
1 2 3 4 5 6 7 8
| err := errors.New("something went wrong")
err = fmt.Errorf("user %d not found", userID)
err = fmt.Errorf("load user: %w", originalErr)
|
errors.Is 与 errors.As
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import "errors"
var ErrNotFound = errors.New("not found")
func find(id int) error { return fmt.Errorf("query db: %w", ErrNotFound) }
err := find(1)
if errors.Is(err, ErrNotFound) { }
var pathErr *os.PathError if errors.As(err, &pathErr) { fmt.Println(pathErr.Path) }
|
自定义错误类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type ValidationError struct { Field string Message string }
func (e *ValidationError) Error() string { return fmt.Sprintf("%s: %s", e.Field, e.Message) }
func validate(u User) error { if u.Name == "" { return &ValidationError{Field: "name", Message: "required"} } return nil }
|
panic 与 recover
panic 引发运行时恐慌,一般用于不可恢复的程序错误;recover 可在 defer 中捕获 panic:
1 2 3 4 5 6 7 8
| func safeDivide(a, b int) (result int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic recovered: %v", r) } }() return a / b, nil }
|
最佳实践:
- 业务逻辑错误 → 返回
error
- 编程错误、不可恢复状态 →
panic(如 init 失败)
- 库代码避免 panic,除非文档明确说明
错误处理风格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| func process() error { if err := step1(); err != nil { return err } if err := step2(); err != nil { return err } return step3() }
func processBad() error { if err := step1(); err == nil { if err := step2(); err == nil { return step3() } else { return err } } else { return err } }
|
小结
- 用
error 返回值处理可预期错误,不要滥用 panic
fmt.Errorf("...: %w", err) 包装错误,保留上下文
errors.Is / errors.As 判断和提取错误
- 自定义错误类型携带更多业务信息