错误处理

基本错误处理

Go语言中的错误处理机制。

package main

import (
    "errors"
    "fmt"
)

// 自定义错误
var (
    ErrNotFound = errors.New("记录未找到")
    ErrInvalidInput = errors.New("无效的输入")
)

// 返回错误的函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为零")
    }
    return a / b, nil
}

func main() {
    // 基本错误处理
    result, err := divide(10, 0)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        return
    }
    fmt.Printf("结果: %v\n", result)
    
    // 错误类型判断
    if err == ErrNotFound {
        fmt.Println("处理未找到错误")
    } else if err == ErrInvalidInput {
        fmt.Println("处理无效输入错误")
    }
}

错误包装

使用fmt.Errorf和errors包包装错误。

package main

import (
    "errors"
    "fmt"
)

// 使用fmt.Errorf包装错误
func processUser(id int) error {
    if id <= 0 {
        return fmt.Errorf("处理用户失败: %w", ErrInvalidInput)
    }
    return nil
}

// 使用errors包包装错误
func validateUser(id int) error {
    if id <= 0 {
        return errors.Wrap(ErrInvalidInput, "验证用户失败")
    }
    return nil
}

func main() {
    // 错误包装示例
    err := processUser(-1)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        // 检查原始错误
        if errors.Is(err, ErrInvalidInput) {
            fmt.Println("是无效输入错误")
        }
    }
    
    // 错误解包
    err = validateUser(-1)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        // 获取原始错误
        if errors.Is(err, ErrInvalidInput) {
            fmt.Println("是无效输入错误")
        }
    }
}

自定义错误类型

创建自定义错误类型。

package main

import (
    "fmt"
    "time"
)

// 自定义错误类型
type ValidationError struct {
    Field   string
    Message string
    Time    time.Time
}

// 实现error接口
func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证错误 [%s]: %s (时间: %v)",
        e.Field, e.Message, e.Time.Format(time.RFC3339))
}

// 创建验证错误
func NewValidationError(field, message string) error {
    return &ValidationError{
        Field:   field,
        Message: message,
        Time:    time.Now(),
    }
}

func main() {
    // 使用自定义错误
    err := NewValidationError("email", "无效的邮箱格式")
    if err != nil {
        fmt.Printf("错误: %v\n", err)
        
        // 类型断言
        if ve, ok := err.(*ValidationError); ok {
            fmt.Printf("字段: %s\n", ve.Field)
            fmt.Printf("消息: %s\n", ve.Message)
            fmt.Printf("时间: %v\n", ve.Time)
        }
    }
}

错误处理最佳实践

错误处理的最佳实践和注意事项。

# 1. 错误检查
- 始终检查错误
- 不要忽略错误
- 在适当的地方处理错误

# 2. 错误信息
- 提供有意义的错误信息
- 包含上下文信息
- 使用清晰的错误描述

# 3. 错误传播
- 在适当的层级处理错误
- 不要过度包装错误
- 保持错误链的简洁

# 4. 错误类型
- 使用自定义错误类型
- 实现error接口
- 提供额外的错误信息

# 5. 错误处理模式
- 使用defer处理清理操作
- 使用panic/recover处理严重错误
- 使用日志记录错误

# 示例
func processFile(filename string) error {
    // 打开文件
    file, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("打开文件失败: %w", err)
    }
    defer file.Close()
    
    // 处理文件
    if err := processContent(file); err != nil {
        return fmt.Errorf("处理文件内容失败: %w", err)
    }
    
    return nil
}

错误处理工具

常用的错误处理工具和库。

# 1. errors包
import "github.com/pkg/errors"

// 包装错误
err = errors.Wrap(err, "处理失败")

// 检查错误
if errors.Is(err, ErrNotFound) {
    // 处理错误
}

// 获取原始错误
originalErr := errors.Cause(err)

# 2. log包
import "log"

// 记录错误
log.Printf("错误: %v", err)
log.Fatal(err)  // 记录错误并退出

# 3. 自定义错误工具
type ErrorCode int

const (
    ErrCodeNotFound ErrorCode = iota + 1
    ErrCodeInvalidInput
    ErrCodeInternal
)

type AppError struct {
    Code    ErrorCode
    Message string
    Err     error
}

func (e *AppError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("%s: %v", e.Message, e.Err)
    }
    return e.Message
}