文章目录
- Go语言通过内置的error接口表示错误: type error interface { Error() string }
- a) errors.New import “errors” func Divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New(“division by zero”) } return a / b, nil } b) fmt.Errorf func ReadFile(path string) ([]byte, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf(“failed to read %s: %v”, path, err) } return data, nil }
- Go标准错误处理范式: result, err := SomeFunction() if err != nil { // 处理错误 return err } // 使用result
- type PathError struct { Op string Path string Err error } func (e *PathError) Error() string { return fmt.Sprintf(“%s %s: %v”, e.Op, e.Path, e.Err) } func OpenConfig(path string) error { if !fileExists(path) { return &PathError{ Op: “open”, Path: path, Err: errors.New(“file not found”), } } // … }
- a) 直接比较 if err == io.EOF { // 处理EOF } b) errors.Is (Go 1.13+) var ErrNotFound = errors.New(“not found”) if errors.Is(err, ErrNotFound) { // 处理特定错误 } c) errors.As (Go 1.13+) var pathErr *PathError if errors.As(err, &pathErr) { fmt.Println(“Failed at path:”, pathErr.Path) }
- func ProcessFile(path string) error { data, err := ReadFile(path) if err != nil { return fmt.Errorf(“process failed: %w”, err) } // … } 解包错误: if err != nil { unwrapped := errors.Unwrap(err) fmt.Println(“Original error:”, unwrapped) }
- 明确错误处理:不要忽略错误 添加上下文:错误信息应有助于调试 区分错误类型:让调用方能区分不同错误 避免过度包装:通常2-3层包装足够 文档化错误:在函数文档中说明可能返回的错误
- a) 忽略错误 data, _ := ReadFile(“config.json”) // 错误! b) 过度包装 // 不好的做法 if err != nil { return fmt.Errorf(“failed: %w”, fmt.Errorf(“processing: %w”, fmt.Errorf(“io: %w”, err))) } c) 滥用panic // 常规错误不应使用panic if x < 0 { panic(“x cannot be negative”) // 应该返回error }
- type MultiError struct { Errors []error } func (m *MultiError) Add(err error) { m.Errors = append(m.Errors, err) } func (m *MultiError) Error() string { var msgs []string for _, err := range m.Errors { msgs = append(msgs, err.Error()) } return strings.Join(msgs, “; “) } func BatchProcess(items []Item) error { var merr MultiError for _, item := range items { if err := process(item); err != nil { merr.Add(err) } } if len(merr.Errors) > 0 { return &merr } return nil }
- func HandleRequest(w http.ResponseWriter, r *http.Request) { err := processRequest(r) if err != nil { // 记录完整错误信息 log.Printf(“request failed: %+v”, err) // 返回简化的错误信息给客户端 http.Error(w, “internal server error”, http.StatusInternalServerError) return } // … }
- 错误预定义 // 预定义错误避免重复分配 var ( ErrInvalidInput = errors.New(“invalid input”) ErrTimeout = errors.New(“operation timeout”) ) func Validate(input string) error { if input == “” { return ErrInvalidInput } // … }
目录
- 一、错误处理基础
- 1. error接口类型
- 2. 创建错误的常用方式
- 3. 错误检查模式
- 二、错误处理进阶
- 1. 自定义错误类型
- 2. 错误判断
- 3. 错误包装(Error Wrapping)
- 三、错误处理实践
- 1. 最佳实践原则
- 2. 常见反模式
- 四、错误处理高级主题
- 1. 错误收集模式
- 2. 错误日志策略
- 3. 性能优化
- 五、错误处理工具和库
- 六、错误处理演进
- 总结
Go语言通过内置的error接口表示错误:
type error interface {
Error() string
}
a) errors.New
import "errors"
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
b) fmt.Errorf
func ReadFile(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %v", path, err)
}
return data, nil
}
Go标准错误处理范式:
result, err := SomeFunction()
if err != nil {
// 处理错误
return err
}
// 使用result
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return fmt.Sprintf("%s %s: %v", e.Op, e.Path, e.Err)
}
func OpenConfig(path string) error {
if !fileExists(path) {
return &PathError{
Op: "open",
Path: path,
Err: errors.New("file not found"),
}
}
// ...
}
a) 直接比较
if err == io.EOF {
// 处理EOF
}
b) errors.Is (Go 1.13+)
var ErrNotFound = errors.New("not found")
if errors.Is(err, ErrNotFound) {
// 处理特定错误
}
c) errors.As (Go 1.13+)
var pathErr *PathError
if errors.As(err, &pathErr) {
fmt.Println("Failed at path:", pathErr.Path)
}
func ProcessFile(path string) error {
data, err := ReadFile(path)
if err != nil {
return fmt.Errorf("process failed: %w", err)
}
// ...
}
解包错误:
if err != nil {
unwrapped := errors.Unwrap(err)
fmt.Println("Original error:", unwrapped)
}
- 明确错误处理:不要忽略错误
- 添加上下文:错误信息应有助于调试
- 区分错误类型:让调用方能区分不同错误
- 避免过度包装:通常2-3层包装足够
- 文档化错误:在函数文档中说明可能返回的错误
a) 忽略错误
data, _ := ReadFile("config.json") // 错误!
b) 过度包装
// 不好的做法
if err != nil {
return fmt.Errorf("failed: %w",
fmt.Errorf("processing: %w",
fmt.Errorf("io: %w", err)))
}
c) 滥用panic
// 常规错误不应使用panic
if x < 0 {
panic("x cannot be negative") // 应该返回error
}
type MultiError struct {
Errors []error
}
func (m *MultiError) Add(err error) {
m.Errors = append(m.Errors, err)
}
func (m *MultiError) Error() string {
var msgs []string
for _, err := range m.Errors {
msgs = append(msgs, err.Error())
}
return strings.Join(msgs, "; ")
}
func BatchProcess(items []Item) error {
var merr MultiError
for _, item := range items {
if err := process(item); err != nil {
merr.Add(err)
}
}
if len(merr.Errors) > 0 {
return &merr
}
return nil
}
func HandleRequest(w http.ResponseWriter, r *http.Request) {
err := processRequest(r)
if err != nil {
// 记录完整错误信息
log.Printf("request failed: %+v", err)
// 返回简化的错误信息给客户端
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
// ...
}
错误预定义
// 预定义错误避免重复分配
var (
ErrInvalidInput = errors.New("invalid input")
ErrTimeout = errors.New("operation timeout")
)
func Validate(input string) error {
if input == "" {
return ErrInvalidInput
}
// ...
}
标准库:
errors:基础错误功能fmt:错误格式化runtime:获取调用栈信息
第三方库:
pkg/errors:增强的错误处理(带堆栈跟踪)hashicorp/errwrap:高级错误包装和解包go.uber.org/multierr:多错误处理
Go 1.13后错误处理的重要改进:
- 正式引入错误包装概念
- 添加
errors.Is、errors.As和errors.Unwrap fmt.Errorf支持%w动词
示例:
func loadConfig() error {
if err := readConfig(); err != nil {
return fmt.Errorf("config load failed: %w", err)
}
return nil
}
func main() {
err := loadConfig()
if errors.Is(err, os.ErrNotExist) {
fmt.Println("配置文件不存在")
}
}
Go的错误处理哲学强调:
- 显式优于隐式:错误必须明确检查
- 简单可预测:没有隐藏的控制流
- 错误即值:错误是普通的值,可以传递和组合
虽然Go的错误处理在初期可能显得冗长,但这种显式的设计带来了:
- 更清晰的代码流程
- 更可靠的错误处理
- 更好的可调试性
掌握Go的错误处理模式是成为优秀Go开发者的关键一步。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持风君子博客。
您可能感兴趣的文章:
- Go语言中的错误处理最佳实践详解
- 重学Go语言之错误处理与异常机制详解
- go语言错误处理基本概念(创建返回)
- Go语言中错误处理的方式总结
- Go语言中实现完美错误处理实践分享