# 第8章:错误处理

学习目标

通过本章学习,你将掌握: - Go语言的错误处理哲学 - error接口和自定义错误类型 - 错误处理的最佳实践 - 错误包装和链式错误 - panic和recover机制 - 错误处理模式和技巧

Go语言的错误处理哲学

错误处理基础

错误作为值

package main

import (
    "errors"
    "fmt"
    "strconv"
)

func main() {
    fmt.Println("=== Go语言错误处理基础 ===")
    
    fmt.Println("\n1. Go错误处理哲学:")
    fmt.Println("   - 错误是值,不是异常")
    fmt.Println("   - 显式错误检查")
    fmt.Println("   - 错误应该被处理,而不是忽略")
    fmt.Println("   - 简单、明确、可预测")
    
    fmt.Println("\n2. error接口:")
    fmt.Println("   type error interface {")
    fmt.Println("       Error() string")
    fmt.Println("   }")
    
    // 演示基本错误处理
    demonstrateBasicErrorHandling()
    
    // 演示错误创建
    demonstrateErrorCreation()
    
    // 演示错误检查模式
    demonstrateErrorCheckingPatterns()
}

func demonstrateBasicErrorHandling() {
    fmt.Println("\n=== 基本错误处理 ===")
    
    // 使用标准库函数的错误处理
    str := "123"
    if num, err := strconv.Atoi(str); err != nil {
        fmt.Printf("转换失败: %v\n", err)
    } else {
        fmt.Printf("转换成功: %d\n", num)
    }
    
    // 错误的字符串转换
    str = "abc"
    if num, err := strconv.Atoi(str); err != nil {
        fmt.Printf("转换失败: %v\n", err)
        fmt.Printf("错误类型: %T\n", err)
    } else {
        fmt.Printf("转换成功: %d\n", num)
    }
    
    // 自定义函数的错误处理
    result, err := divide(10, 2)
    if err != nil {
        fmt.Printf("除法错误: %v\n", err)
    } else {
        fmt.Printf("10 / 2 = %.2f\n", result)
    }
    
    result, err = divide(10, 0)
    if err != nil {
        fmt.Printf("除法错误: %v\n", err)
    } else {
        fmt.Printf("10 / 0 = %.2f\n", result)
    }
}

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

func demonstrateErrorCreation() {
    fmt.Println("\n=== 错误创建方式 ===")
    
    // 1. 使用errors.New
    err1 := errors.New("这是一个简单错误")
    fmt.Printf("errors.New: %v\n", err1)
    
    // 2. 使用fmt.Errorf
    name := "Alice"
    age := -5
    err2 := fmt.Errorf("用户 %s 的年龄 %d 无效", name, age)
    fmt.Printf("fmt.Errorf: %v\n", err2)
    
    // 3. 自定义错误类型
    err3 := &ValidationError{
        Field:   "email",
        Value:   "invalid-email",
        Message: "邮箱格式不正确",
    }
    fmt.Printf("自定义错误: %v\n", err3)
    
    // 4. 错误包装
    originalErr := errors.New("原始错误")
    wrappedErr := fmt.Errorf("包装错误: %w", originalErr)
    fmt.Printf("包装错误: %v\n", wrappedErr)
}

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

func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证失败 [%s=%s]: %s", e.Field, e.Value, e.Message)
}

func demonstrateErrorCheckingPatterns() {
    fmt.Println("\n=== 错误检查模式 ===")
    
    // 模式1:立即检查
    if result, err := riskyOperation("success"); err != nil {
        fmt.Printf("操作失败: %v\n", err)
    } else {
        fmt.Printf("操作成功: %s\n", result)
    }
    
    // 模式2:延迟检查(不推荐)
    result, err := riskyOperation("fail")
    if err != nil {
        fmt.Printf("操作失败: %v\n", err)
        return
    }
    fmt.Printf("操作成功: %s\n", result)
}

func riskyOperation(mode string) (string, error) {
    if mode == "fail" {
        return "", errors.New("操作失败")
    }
    return "操作结果", nil
}
go run error_basics.go

错误处理最佳实践

错误处理原则

package main

import (
    "errors"
    "fmt"
    "io"
    "os"
    "time"
)

func main() {
    fmt.Println("=== 错误处理最佳实践 ===")
    
    // 1. 总是检查错误
    demonstrateAlwaysCheckErrors()
    
    // 2. 提供有意义的错误信息
    demonstrateMeaningfulErrors()
    
    // 3. 错误处理策略
    demonstrateErrorStrategies()
    
    // 4. 错误传播
    demonstrateErrorPropagation()
}

func demonstrateAlwaysCheckErrors() {
    fmt.Println("\n=== 1. 总是检查错误 ===")
    
    // 好的做法:检查每个可能的错误
    filename := "test.txt"
    
    // 创建文件
    file, err := os.Create(filename)
    if err != nil {
        fmt.Printf("创建文件失败: %v\n", err)
        return
    }
    defer func() {
        if closeErr := file.Close(); closeErr != nil {
            fmt.Printf("关闭文件失败: %v\n", closeErr)
        }
    }()
    
    // 写入数据
    data := "Hello, Error Handling!"
    if _, err := file.WriteString(data); err != nil {
        fmt.Printf("写入文件失败: %v\n", err)
        return
    }
    
    fmt.Printf("成功写入文件: %s\n", filename)
    
    // 清理
    if err := os.Remove(filename); err != nil {
        fmt.Printf("删除文件失败: %v\n", err)
    }
}

func demonstrateMeaningfulErrors() {
    fmt.Println("\n=== 2. 提供有意义的错误信息 ===")
    
    // 不好的错误信息
    _, err := badFunction("")
    if err != nil {
        fmt.Printf("不好的错误: %v\n", err)
    }
    
    // 好的错误信息
    _, err = goodFunction("")
    if err != nil {
        fmt.Printf("好的错误: %v\n", err)
    }
    
    // 带上下文的错误
    err = processUser(User{ID: 0, Name: "", Email: "invalid"})
    if err != nil {
        fmt.Printf("处理用户错误: %v\n", err)
    }
}

func badFunction(input string) (string, error) {
    if input == "" {
        return "", errors.New("错误")
    }
    return input, nil
}

func goodFunction(input string) (string, error) {
    if input == "" {
        return "", errors.New("输入参数不能为空字符串")
    }
    return input, nil
}

type User struct {
    ID    int
    Name  string
    Email string
}

func processUser(user User) error {
    if user.ID <= 0 {
        return fmt.Errorf("用户ID无效: %d (必须大于0)", user.ID)
    }
    
    if user.Name == "" {
        return fmt.Errorf("用户名不能为空 (用户ID: %d)", user.ID)
    }
    
    if !isValidEmail(user.Email) {
        return fmt.Errorf("邮箱格式无效: %s (用户: %s, ID: %d)", 
            user.Email, user.Name, user.ID)
    }
    
    return nil
}

func isValidEmail(email string) bool {
    return len(email) > 0 && email != "invalid"
}

func demonstrateErrorStrategies() {
    fmt.Println("\n=== 3. 错误处理策略 ===")
    
    // 策略1:立即返回错误
    fmt.Println("\n策略1:立即返回错误")
    if err := strategyFailFast(); err != nil {
        fmt.Printf("快速失败: %v\n", err)
    }
    
    // 策略2:重试机制
    fmt.Println("\n策略2:重试机制")
    if result, err := strategyRetry(); err != nil {
        fmt.Printf("重试后仍失败: %v\n", err)
    } else {
        fmt.Printf("重试成功: %s\n", result)
    }
    
    // 策略3:降级处理
    fmt.Println("\n策略3:降级处理")
    result := strategyFallback()
    fmt.Printf("降级结果: %s\n", result)
    
    // 策略4:记录并继续
    fmt.Println("\n策略4:记录并继续")
    strategyLogAndContinue()
}

func strategyFailFast() error {
    // 模拟一个会失败的操作
    if err := simulateFailure(); err != nil {
        return fmt.Errorf("操作失败,立即返回: %w", err)
    }
    return nil
}

func strategyRetry() (string, error) {
    maxRetries := 3
    for i := 0; i < maxRetries; i++ {
        if result, err := simulateUnstableOperation(i); err != nil {
            fmt.Printf("第 %d 次尝试失败: %v\n", i+1, err)
            if i == maxRetries-1 {
                return "", fmt.Errorf("重试 %d 次后仍然失败: %w", maxRetries, err)
            }
            time.Sleep(100 * time.Millisecond) // 等待后重试
        } else {
            return result, nil
        }
    }
    return "", errors.New("不应该到达这里")
}

func strategyFallback() string {
    // 尝试主要操作
    if result, err := primaryOperation(); err != nil {
        fmt.Printf("主要操作失败: %v,使用备用方案\n", err)
        return fallbackOperation()
    } else {
        return result
    }
}

func strategyLogAndContinue() {
    operations := []string{"op1", "op2", "op3"}
    
    for _, op := range operations {
        if err := simulateOperation(op); err != nil {
            // 记录错误但继续处理其他操作
            fmt.Printf("操作 %s 失败: %v (继续处理其他操作)\n", op, err)
        } else {
            fmt.Printf("操作 %s 成功\n", op)
        }
    }
}

func simulateFailure() error {
    return errors.New("模拟失败")
}

func simulateUnstableOperation(attempt int) (string, error) {
    if attempt < 2 {
        return "", fmt.Errorf("不稳定操作失败 (尝试 %d)", attempt+1)
    }
    return "成功结果", nil
}

func primaryOperation() (string, error) {
    return "", errors.New("主要操作失败")
}

func fallbackOperation() string {
    return "备用操作结果"
}

func simulateOperation(name string) error {
    if name == "op2" {
        return fmt.Errorf("操作 %s 失败", name)
    }
    return nil
}

func demonstrateErrorPropagation() {
    fmt.Println("\n=== 4. 错误传播 ===")
    
    // 演示错误在调用栈中的传播
    if err := levelOne(); err != nil {
        fmt.Printf("顶层捕获错误: %v\n", err)
    }
}

func levelOne() error {
    if err := levelTwo(); err != nil {
        return fmt.Errorf("level1 处理失败: %w", err)
    }
    return nil
}

func levelTwo() error {
    if err := levelThree(); err != nil {
        return fmt.Errorf("level2 处理失败: %w", err)
    }
    return nil
}

func levelThree() error {
    return errors.New("level3 底层错误")
}
go run error_best_practices.go

自定义错误类型

创建自定义错误

实现error接口

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("=== 自定义错误类型 ===")
    
    // 演示不同类型的自定义错误
    demonstrateCustomErrors()
    
    // 演示错误类型断言
    demonstrateErrorTypeAssertion()
    
    // 演示错误分类
    demonstrateErrorClassification()
}

// 1. 简单自定义错误
type SimpleError struct {
    Message string
    Code    int
}

func (e SimpleError) Error() string {
    return fmt.Sprintf("错误 [%d]: %s", e.Code, e.Message)
}

// 2. 带时间戳的错误
type TimestampedError struct {
    Message   string
    Timestamp time.Time
    Component string
}

func (e TimestampedError) Error() string {
    return fmt.Sprintf("[%s] %s: %s", 
        e.Timestamp.Format("2006-01-02 15:04:05"), 
        e.Component, e.Message)
}

// 3. 网络错误
type NetworkError struct {
    Op       string // 操作类型
    Network  string // 网络类型
    Address  string // 地址
    Err      error  // 底层错误
    Timeout  bool   // 是否超时
    Temporary bool  // 是否临时错误
}

func (e *NetworkError) Error() string {
    return fmt.Sprintf("%s %s %s: %v", e.Op, e.Network, e.Address, e.Err)
}

func (e *NetworkError) IsTimeout() bool {
    return e.Timeout
}

func (e *NetworkError) IsTemporary() bool {
    return e.Temporary
}

// 4. 验证错误(包含多个字段错误)
type ValidationError struct {
    Errors map[string]string
}

func (e ValidationError) Error() string {
    if len(e.Errors) == 0 {
        return "验证失败"
    }
    
    msg := "验证失败:\n"
    for field, err := range e.Errors {
        msg += fmt.Sprintf("  %s: %s\n", field, err)
    }
    return msg
}

func (e ValidationError) HasErrors() bool {
    return len(e.Errors) > 0
}

func (e ValidationError) GetError(field string) (string, bool) {
    err, exists := e.Errors[field]
    return err, exists
}

// 5. 业务错误
type BusinessError struct {
    Code    string
    Message string
    Details map[string]interface{}
}

func (e BusinessError) Error() string {
    return fmt.Sprintf("业务错误 [%s]: %s", e.Code, e.Message)
}

func (e BusinessError) GetCode() string {
    return e.Code
}

func (e BusinessError) GetDetails() map[string]interface{} {
    return e.Details
}

func demonstrateCustomErrors() {
    fmt.Println("\n=== 自定义错误演示 ===")
    
    // 简单错误
    err1 := SimpleError{Message: "文件不存在", Code: 404}
    fmt.Printf("简单错误: %v\n", err1)
    
    // 带时间戳的错误
    err2 := TimestampedError{
        Message:   "数据库连接失败",
        Timestamp: time.Now(),
        Component: "Database",
    }
    fmt.Printf("时间戳错误: %v\n", err2)
    
    // 网络错误
    err3 := &NetworkError{
        Op:        "dial",
        Network:   "tcp",
        Address:   "localhost:8080",
        Err:       fmt.Errorf("connection refused"),
        Timeout:   false,
        Temporary: true,
    }
    fmt.Printf("网络错误: %v\n", err3)
    fmt.Printf("是否超时: %t\n", err3.IsTimeout())
    fmt.Printf("是否临时: %t\n", err3.IsTemporary())
    
    // 验证错误
    err4 := ValidationError{
        Errors: map[string]string{
            "name":  "名称不能为空",
            "email": "邮箱格式不正确",
            "age":   "年龄必须大于0",
        },
    }
    fmt.Printf("验证错误: %v\n", err4)
    
    // 业务错误
    err5 := BusinessError{
        Code:    "INSUFFICIENT_BALANCE",
        Message: "账户余额不足",
        Details: map[string]interface{}{
            "balance": 100.0,
            "required": 150.0,
            "account": "12345",
        },
    }
    fmt.Printf("业务错误: %v\n", err5)
}

func demonstrateErrorTypeAssertion() {
    fmt.Println("\n=== 错误类型断言 ===")
    
    errors := []error{
        SimpleError{Message: "简单错误", Code: 500},
        &NetworkError{Op: "read", Network: "tcp", Address: "example.com:80", Timeout: true},
        ValidationError{Errors: map[string]string{"field": "invalid"}},
        BusinessError{Code: "AUTH_FAILED", Message: "认证失败"},
    }
    
    for i, err := range errors {
        fmt.Printf("\n错误 %d: %v\n", i+1, err)
        
        // 类型断言
        switch e := err.(type) {
        case SimpleError:
            fmt.Printf("  类型: 简单错误, 代码: %d\n", e.Code)
        case *NetworkError:
            fmt.Printf("  类型: 网络错误, 操作: %s\n", e.Op)
            if e.IsTimeout() {
                fmt.Printf("  这是一个超时错误\n")
            }
        case ValidationError:
            fmt.Printf("  类型: 验证错误, 字段数: %d\n", len(e.Errors))
        case BusinessError:
            fmt.Printf("  类型: 业务错误, 代码: %s\n", e.GetCode())
        default:
            fmt.Printf("  类型: 未知错误类型\n")
        }
    }
}

func demonstrateErrorClassification() {
    fmt.Println("\n=== 错误分类处理 ===")
    
    // 模拟不同类型的操作错误
    operations := []func() error{
        func() error { return simulateFileOperation() },
        func() error { return simulateNetworkOperation() },
        func() error { return simulateValidationOperation() },
        func() error { return simulateBusinessOperation() },
    }
    
    for i, op := range operations {
        fmt.Printf("\n执行操作 %d:\n", i+1)
        if err := op(); err != nil {
            handleError(err)
        } else {
            fmt.Printf("  操作成功\n")
        }
    }
}

func simulateFileOperation() error {
    return SimpleError{Message: "文件权限不足", Code: 403}
}

func simulateNetworkOperation() error {
    return &NetworkError{
        Op:        "connect",
        Network:   "tcp",
        Address:   "api.example.com:443",
        Err:       fmt.Errorf("timeout"),
        Timeout:   true,
        Temporary: true,
    }
}

func simulateValidationOperation() error {
    return ValidationError{
        Errors: map[string]string{
            "username": "用户名已存在",
            "password": "密码强度不够",
        },
    }
}

func simulateBusinessOperation() error {
    return BusinessError{
        Code:    "QUOTA_EXCEEDED",
        Message: "已超出配额限制",
        Details: map[string]interface{}{
            "current": 1000,
            "limit":   500,
            "period":  "daily",
        },
    }
}

func handleError(err error) {
    fmt.Printf("  错误: %v\n", err)
    
    switch e := err.(type) {
    case SimpleError:
        if e.Code >= 500 {
            fmt.Printf("  处理策略: 服务器错误,记录日志并通知管理员\n")
        } else if e.Code >= 400 {
            fmt.Printf("  处理策略: 客户端错误,返回错误信息\n")
        }
        
    case *NetworkError:
        if e.IsTimeout() {
            fmt.Printf("  处理策略: 网络超时,建议重试\n")
        } else if e.IsTemporary() {
            fmt.Printf("  处理策略: 临时网络错误,稍后重试\n")
        } else {
            fmt.Printf("  处理策略: 永久网络错误,检查网络配置\n")
        }
        
    case ValidationError:
        fmt.Printf("  处理策略: 验证错误,返回详细的字段错误信息\n")
        for field, msg := range e.Errors {
            fmt.Printf("    %s: %s\n", field, msg)
        }
        
    case BusinessError:
        fmt.Printf("  处理策略: 业务错误,根据错误代码执行相应逻辑\n")
        fmt.Printf("    错误代码: %s\n", e.GetCode())
        if details := e.GetDetails(); len(details) > 0 {
            fmt.Printf("    详细信息: %+v\n", details)
        }
        
    default:
        fmt.Printf("  处理策略: 未知错误类型,使用默认处理\n")
    }
}
go run custom_errors.go

错误包装和链式错误

错误包装

使用fmt.Errorf和%w动词

package main

import (
    "errors"
    "fmt"
    "os"
)

func main() {
    fmt.Println("=== 错误包装和链式错误 ===")
    
    // 演示错误包装
    demonstrateErrorWrapping()
    
    // 演示错误解包
    demonstrateErrorUnwrapping()
    
    // 演示错误链
    demonstrateErrorChain()
    
    // 演示errors.Is和errors.As
    demonstrateErrorsIsAs()
}

func demonstrateErrorWrapping() {
    fmt.Println("\n=== 错误包装 ===")
    
    // 原始错误
    originalErr := errors.New("数据库连接失败")
    fmt.Printf("原始错误: %v\n", originalErr)
    
    // 包装错误(添加上下文)
    wrappedErr := fmt.Errorf("用户服务初始化失败: %w", originalErr)
    fmt.Printf("包装错误: %v\n", wrappedErr)
    
    // 多层包装
    doubleWrappedErr := fmt.Errorf("应用启动失败: %w", wrappedErr)
    fmt.Printf("双重包装: %v\n", doubleWrappedErr)
    
    // 检查错误链
    fmt.Printf("\n错误链检查:\n")
    if errors.Is(doubleWrappedErr, originalErr) {
        fmt.Printf("  双重包装错误包含原始错误\n")
    }
    
    // 获取根本原因
    rootCause := errors.Unwrap(errors.Unwrap(doubleWrappedErr))
    fmt.Printf("  根本原因: %v\n", rootCause)
}

func demonstrateErrorUnwrapping() {
    fmt.Println("\n=== 错误解包 ===")
    
    // 创建错误链
    err := createErrorChain()
    fmt.Printf("完整错误: %v\n", err)
    
    // 逐层解包
    fmt.Printf("\n逐层解包:\n")
    current := err
    level := 1
    
    for current != nil {
        fmt.Printf("  层级 %d: %v\n", level, current)
        current = errors.Unwrap(current)
        level++
    }
}

func createErrorChain() error {
    // 模拟深层调用栈中的错误
    if err := databaseOperation(); err != nil {
        return fmt.Errorf("业务逻辑处理失败: %w", err)
    }
    return nil
}

func databaseOperation() error {
    if err := connectionPool(); err != nil {
        return fmt.Errorf("数据库操作失败: %w", err)
    }
    return nil
}

func connectionPool() error {
    if err := networkCall(); err != nil {
        return fmt.Errorf("连接池获取连接失败: %w", err)
    }
    return nil
}

func networkCall() error {
    return &NetworkError{
        Op:      "dial",
        Network: "tcp",
        Address: "db.example.com:5432",
        Err:     errors.New("connection refused"),
        Timeout: false,
    }
}

// 自定义网络错误(支持Unwrap)
type NetworkError struct {
    Op      string
    Network string
    Address string
    Err     error
    Timeout bool
}

func (e *NetworkError) Error() string {
    return fmt.Sprintf("%s %s %s: %v", e.Op, e.Network, e.Address, e.Err)
}

// 实现Unwrap方法以支持错误链
func (e *NetworkError) Unwrap() error {
    return e.Err
}

func demonstrateErrorChain() {
    fmt.Println("\n=== 错误链处理 ===")
    
    // 创建复杂的错误链
    err := processRequest("invalid-request")
    if err != nil {
        fmt.Printf("请求处理失败: %v\n", err)
        
        // 分析错误链
        analyzeErrorChain(err)
    }
}

func processRequest(requestID string) error {
    if err := validateRequest(requestID); err != nil {
        return fmt.Errorf("请求处理失败 [ID: %s]: %w", requestID, err)
    }
    return nil
}

func validateRequest(requestID string) error {
    if err := checkPermissions(requestID); err != nil {
        return fmt.Errorf("请求验证失败: %w", err)
    }
    return nil
}

func checkPermissions(requestID string) error {
    if requestID == "invalid-request" {
        return &PermissionError{
            User:     "anonymous",
            Resource: "protected-resource",
            Action:   "read",
            Reason:   "用户未认证",
        }
    }
    return nil
}

// 权限错误
type PermissionError struct {
    User     string
    Resource string
    Action   string
    Reason   string
}

func (e *PermissionError) Error() string {
    return fmt.Sprintf("权限拒绝: 用户 %s 无法对资源 %s 执行 %s 操作 (%s)", 
        e.User, e.Resource, e.Action, e.Reason)
}

func analyzeErrorChain(err error) {
    fmt.Printf("\n错误链分析:\n")
    
    // 检查特定错误类型
    var netErr *NetworkError
    if errors.As(err, &netErr) {
        fmt.Printf("  包含网络错误: %s %s\n", netErr.Op, netErr.Address)
    }
    
    var permErr *PermissionError
    if errors.As(err, &permErr) {
        fmt.Printf("  包含权限错误: 用户 %s, 资源 %s\n", permErr.User, permErr.Resource)
    }
    
    // 检查特定错误值
    if errors.Is(err, os.ErrNotExist) {
        fmt.Printf("  包含文件不存在错误\n")
    }
}

func demonstrateErrorsIsAs() {
    fmt.Println("\n=== errors.Is 和 errors.As 使用 ===")
    
    // 创建包含不同错误类型的错误链
    errors_list := []error{
        createNetworkErrorChain(),
        createPermissionErrorChain(),
        createFileErrorChain(),
    }
    
    for i, err := range errors_list {
        fmt.Printf("\n错误 %d: %v\n", i+1, err)
        
        // 使用 errors.Is 检查特定错误值
        if errors.Is(err, os.ErrNotExist) {
            fmt.Printf("  ✓ 包含文件不存在错误\n")
        }
        
        if errors.Is(err, os.ErrPermission) {
            fmt.Printf("  ✓ 包含权限错误\n")
        }
        
        // 使用 errors.As 提取特定错误类型
        var netErr *NetworkError
        if errors.As(err, &netErr) {
            fmt.Printf("  ✓ 包含网络错误: %s\n", netErr.Address)
        }
        
        var permErr *PermissionError
        if errors.As(err, &permErr) {
            fmt.Printf("  ✓ 包含权限错误: %s -> %s\n", permErr.User, permErr.Resource)
        }
    }
}

func createNetworkErrorChain() error {
    netErr := &NetworkError{
        Op:      "connect",
        Network: "tcp",
        Address: "api.example.com:443",
        Err:     errors.New("timeout"),
        Timeout: true,
    }
    return fmt.Errorf("服务调用失败: %w", netErr)
}

func createPermissionErrorChain() error {
    permErr := &PermissionError{
        User:     "guest",
        Resource: "admin-panel",
        Action:   "access",
        Reason:   "权限不足",
    }
    return fmt.Errorf("访问控制失败: %w", permErr)
}

func createFileErrorChain() error {
    return fmt.Errorf("配置加载失败: %w", 
        fmt.Errorf("读取配置文件失败: %w", os.ErrNotExist))
}
go run error_wrapping.go

panic和recover机制

理解panic和recover

panic的使用场景

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Println("=== panic和recover机制 ===")
    
    // 演示panic的基本使用
    demonstratePanicBasics()
    
    // 演示recover的使用
    demonstrateRecover()
    
    // 演示panic的传播
    demonstratePanicPropagation()
    
    // 演示实际应用场景
    demonstratePracticalUsage()
}

func demonstratePanicBasics() {
    fmt.Println("\n=== panic基础 ===")
    
    fmt.Println("1. panic的使用场景:")
    fmt.Println("   - 程序遇到无法恢复的错误")
    fmt.Println("   - 编程错误(如数组越界)")
    fmt.Println("   - 不应该发生的情况")
    fmt.Println("   - 初始化失败")
    
    fmt.Println("\n2. panic vs error:")
    fmt.Println("   - error: 预期的、可处理的错误")
    fmt.Println("   - panic: 意外的、程序无法继续的情况")
    
    // 演示会导致panic的情况
    fmt.Println("\n3. 常见panic情况:")
    
    // 使用defer和recover来安全演示panic
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   捕获到panic: %v\n", r)
        }
    }()
    
    // 这些操作会导致panic,但被recover捕获
    demonstratePanicCases()
}

func demonstratePanicCases() {
    cases := []struct {
        name string
        fn   func()
    }{
        {"数组越界", func() {
            arr := [3]int{1, 2, 3}
            _ = arr[5] // 越界访问
        }},
        {"空指针解引用", func() {
            var p *int
            _ = *p // 空指针解引用
        }},
        {"类型断言失败", func() {
            var i interface{} = "string"
            _ = i.(int) // 错误的类型断言
        }},
        {"向已关闭的channel发送", func() {
            ch := make(chan int)
            close(ch)
            ch <- 1 // 向已关闭的channel发送
        }},
    }
    
    for _, c := range cases {
        func() {
            defer func() {
                if r := recover(); r != nil {
                    fmt.Printf("   %s: %v\n", c.name, r)
                }
            }()
            c.fn()
        }()
    }
}

func demonstrateRecover() {
    fmt.Println("\n=== recover使用 ===")
    
    fmt.Println("\n1. recover的规则:")
    fmt.Println("   - 只能在defer函数中调用")
    fmt.Println("   - 只能恢复当前goroutine的panic")
    fmt.Println("   - 返回panic的值,如果没有panic返回nil")
    
    // 演示正确的recover使用
    fmt.Println("\n2. 正确的recover使用:")
    
    result := safeOperation("valid")
    fmt.Printf("   安全操作结果: %s\n", result)
    
    result = safeOperation("panic")
    fmt.Printf("   安全操作结果: %s\n", result)
    
    // 演示错误的recover使用
    fmt.Println("\n3. 错误的recover使用:")
    incorrectRecoverUsage()
}

func safeOperation(input string) (result string) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   捕获panic: %v\n", r)
            result = "操作失败,已恢复"
        }
    }()
    
    if input == "panic" {
        panic("模拟panic情况")
    }
    
    return "操作成功"
}

func incorrectRecoverUsage() {
    // 错误1:不在defer中调用recover
    if r := recover(); r != nil {
        fmt.Printf("   这个recover不会工作: %v\n", r)
    }
    
    // 错误2:在defer中但不是直接调用
    defer func() {
        go func() {
            if r := recover(); r != nil {
                fmt.Printf("   这个recover也不会工作: %v\n", r)
            }
        }()
    }()
    
    fmt.Println("   错误的recover使用不会捕获panic")
}

func demonstratePanicPropagation() {
    fmt.Println("\n=== panic传播 ===")
    
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("main函数捕获panic: %v\n", r)
            
            // 打印调用栈
            buf := make([]byte, 1024)
            n := runtime.Stack(buf, false)
            fmt.Printf("调用栈:\n%s\n", buf[:n])
        }
    }()
    
    fmt.Println("调用函数链...")
    functionA()
}

func functionA() {
    fmt.Println("进入函数A")
    defer fmt.Println("退出函数A")
    functionB()
}

func functionB() {
    fmt.Println("进入函数B")
    defer fmt.Println("退出函数B")
    functionC()
}

func functionC() {
    fmt.Println("进入函数C")
    defer fmt.Println("退出函数C")
    panic("函数C中发生panic")
}

func demonstratePracticalUsage() {
    fmt.Println("\n=== 实际应用场景 ===")
    
    // 1. Web服务器中的panic恢复
    fmt.Println("\n1. Web服务器panic恢复:")
    simulateWebHandler("normal")
    simulateWebHandler("panic")
    
    // 2. 工作池中的panic恢复
    fmt.Println("\n2. 工作池panic恢复:")
    simulateWorkerPool()
    
    // 3. 资源清理
    fmt.Println("\n3. 资源清理:")
    simulateResourceManagement()
}

func simulateWebHandler(requestType string) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   HTTP处理器panic恢复: %v\n", r)
            fmt.Printf("   返回500错误给客户端\n")
        }
    }()
    
    fmt.Printf("   处理请求: %s\n", requestType)
    
    if requestType == "panic" {
        panic("处理请求时发生错误")
    }
    
    fmt.Printf("   请求处理成功\n")
}

func simulateWorkerPool() {
    jobs := []string{"job1", "panic-job", "job3"}
    
    for _, job := range jobs {
        func(jobName string) {
            defer func() {
                if r := recover(); r != nil {
                    fmt.Printf("   工作任务 %s panic恢复: %v\n", jobName, r)
                    fmt.Printf("   工作池继续运行\n")
                }
            }()
            
            fmt.Printf("   执行任务: %s\n", jobName)
            
            if jobName == "panic-job" {
                panic("任务执行失败")
            }
            
            fmt.Printf("   任务 %s 完成\n", jobName)
        }(job)
    }
}

func simulateResourceManagement() {
    resource := acquireResource()
    defer func() {
        releaseResource(resource)
        if r := recover(); r != nil {
            fmt.Printf("   资源管理panic恢复: %v\n", r)
            fmt.Printf("   资源已安全释放\n")
        }
    }()
    
    fmt.Printf("   使用资源进行操作\n")
    
    // 模拟操作中的panic
    panic("操作过程中发生错误")
}

func acquireResource() string {
    fmt.Printf("   获取资源\n")
    return "database-connection"
}

func releaseResource(resource string) {
    fmt.Printf("   释放资源: %s\n", resource)
}
go run panic_recover.go

panic和recover的最佳实践

何时使用panic

package main

import (
    "fmt"
    "log"
    "os"
    "time"
)

func main() {
    fmt.Println("=== panic和recover最佳实践 ===")
    
    // 演示何时使用panic
    demonstrateWhenToPanic()
    
    // 演示何时不使用panic
    demonstrateWhenNotToPanic()
    
    // 演示panic的替代方案
    demonstrateAlternativesToPanic()
    
    // 演示库设计中的panic使用
    demonstrateLibraryPanicUsage()
}

func demonstrateWhenToPanic() {
    fmt.Println("\n=== 何时使用panic ===")
    
    fmt.Println("\n1. 程序初始化失败:")
    // 模拟配置加载失败
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   捕获初始化panic: %v\n", r)
        }
    }()
    
    // initializeApp() // 这会panic,被上面的recover捕获
    
    fmt.Println("\n2. 编程错误(断言失败):")
    demonstrateAssertions()
    
    fmt.Println("\n3. 不可能的情况:")
    demonstrateImpossibleCases()
}

func initializeApp() {
    // 模拟关键配置加载失败
    if !loadCriticalConfig() {
        panic("无法加载关键配置,程序无法继续运行")
    }
}

func loadCriticalConfig() bool {
    // 模拟配置加载失败
    return false
}

func demonstrateAssertions() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   断言失败: %v\n", r)
        }
    }()
    
    // 使用断言检查编程错误
    value := 42
    assert(value > 0, "值必须为正数")
    assert(value < 100, "值必须小于100")
    assert(value == 50, "值必须等于50") // 这个断言会失败
}

func assert(condition bool, message string) {
    if !condition {
        panic(fmt.Sprintf("断言失败: %s", message))
    }
}

func demonstrateImpossibleCases() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   不可能情况: %v\n", r)
        }
    }()
    
    status := getStatus()
    switch status {
    case "active":
        fmt.Printf("   状态: 活跃\n")
    case "inactive":
        fmt.Printf("   状态: 非活跃\n")
    case "pending":
        fmt.Printf("   状态: 待处理\n")
    default:
        // 理论上不应该到达这里
        panic(fmt.Sprintf("未知状态: %s", status))
    }
}

func getStatus() string {
    return "unknown" // 模拟返回未知状态
}

func demonstrateWhenNotToPanic() {
    fmt.Println("\n=== 何时不使用panic ===")
    
    fmt.Println("\n1. 可预期的错误(应该使用error):")
    
    // 好的做法:使用error
    if err := processFile("nonexistent.txt"); err != nil {
        fmt.Printf("   文件处理错误: %v\n", err)
    }
    
    // 不好的做法:使用panic(这里用defer+recover演示)
    func() {
        defer func() {
            if r := recover(); r != nil {
                fmt.Printf("   不当的panic使用: %v\n", r)
            }
        }()
        processFileWithPanic("nonexistent.txt")
    }()
    
    fmt.Println("\n2. 网络错误(应该使用error):")
    
    // 好的做法
    if data, err := fetchData("http://example.com"); err != nil {
        fmt.Printf("   网络请求错误: %v\n", err)
    } else {
        fmt.Printf("   获取数据: %s\n", data)
    }
    
    fmt.Println("\n3. 用户输入错误(应该使用error):")
    
    // 好的做法
    if err := validateUserInput(""); err != nil {
        fmt.Printf("   输入验证错误: %v\n", err)
    }
}

func processFile(filename string) error {
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        return fmt.Errorf("文件不存在: %s", filename)
    }
    return nil
}

func processFileWithPanic(filename string) {
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        panic(fmt.Sprintf("文件不存在: %s", filename)) // 不好的做法
    }
}

func fetchData(url string) (string, error) {
    // 模拟网络请求
    return "", fmt.Errorf("连接超时")
}

func validateUserInput(input string) error {
    if input == "" {
        return fmt.Errorf("输入不能为空")
    }
    return nil
}

func demonstrateAlternativesToPanic() {
    fmt.Println("\n=== panic的替代方案 ===")
    
    fmt.Println("\n1. 使用error返回:")
    if result, err := safeCalculation(10, 0); err != nil {
        fmt.Printf("   计算错误: %v\n", err)
    } else {
        fmt.Printf("   计算结果: %f\n", result)
    }
    
    fmt.Println("\n2. 使用bool返回:")
    if result, ok := tryOperation(); !ok {
        fmt.Printf("   操作失败\n")
    } else {
        fmt.Printf("   操作成功: %s\n", result)
    }
    
    fmt.Println("\n3. 使用Option模式:")
    result := findUser("nonexistent")
    if result.IsNone() {
        fmt.Printf("   用户不存在\n")
    } else {
        fmt.Printf("   找到用户: %s\n", result.Value())
    }
    
    fmt.Println("\n4. 使用默认值:")
    config := getConfigWithDefault("missing-key")
    fmt.Printf("   配置值: %s\n", config)
}

func safeCalculation(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为零")
    }
    return a / b, nil
}

func tryOperation() (string, bool) {
    // 模拟可能失败的操作
    success := false
    if success {
        return "操作结果", true
    }
    return "", false
}

// Option模式实现
type Option struct {
    value string
    hasValue bool
}

func Some(value string) Option {
    return Option{value: value, hasValue: true}
}

func None() Option {
    return Option{hasValue: false}
}

func (o Option) IsNone() bool {
    return !o.hasValue
}

func (o Option) Value() string {
    if !o.hasValue {
        panic("试图获取None的值")
    }
    return o.value
}

func findUser(username string) Option {
    users := map[string]string{
        "alice": "Alice Smith",
        "bob":   "Bob Jones",
    }
    
    if name, exists := users[username]; exists {
        return Some(name)
    }
    return None()
}

func getConfigWithDefault(key string) string {
    config := map[string]string{
        "host": "localhost",
        "port": "8080",
    }
    
    if value, exists := config[key]; exists {
        return value
    }
    return "default-value" // 返回默认值而不是panic
}

func demonstrateLibraryPanicUsage() {
    fmt.Println("\n=== 库设计中的panic使用 ===")
    
    fmt.Println("\n1. 提供panic和non-panic版本:")
    
    // non-panic版本
    if result, err := SafeParseInt("123"); err != nil {
        fmt.Printf("   安全解析失败: %v\n", err)
    } else {
        fmt.Printf("   安全解析成功: %d\n", result)
    }
    
    // panic版本(用于确定输入有效的场景)
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("   MustParseInt panic: %v\n", r)
        }
    }()
    
    result1 := MustParseInt("456")
    fmt.Printf("   MustParseInt成功: %d\n", result1)
    
    result2 := MustParseInt("invalid") // 这会panic
    fmt.Printf("   这行不会执行: %d\n", result2)
}

func SafeParseInt(s string) (int, error) {
    // 安全版本,返回error
    if s == "invalid" {
        return 0, fmt.Errorf("无效的整数格式: %s", s)
    }
    
    // 简化的解析逻辑
    if s == "123" {
        return 123, nil
    }
    if s == "456" {
        return 456, nil
    }
    
    return 0, fmt.Errorf("无法解析: %s", s)
}

func MustParseInt(s string) int {
    // panic版本,用于确定输入有效的场景
    result, err := SafeParseInt(s)
    if err != nil {
        panic(fmt.Sprintf("MustParseInt失败: %v", err))
    }
    return result
}
go run panic_best_practices.go

错误处理模式

常见错误处理模式

错误聚合和批处理

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Println("=== 错误处理模式 ===")
    
    // 演示错误聚合
    demonstrateErrorAggregation()
    
    // 演示错误转换
    demonstrateErrorTransformation()
    
    // 演示错误上下文
    demonstrateErrorContext()
    
    // 演示错误分类处理
    demonstrateErrorClassification()
}

// 错误聚合器
type ErrorAggregator struct {
    errors []error
}

func NewErrorAggregator() *ErrorAggregator {
    return &ErrorAggregator{}
}

func (ea *ErrorAggregator) Add(err error) {
    if err != nil {
        ea.errors = append(ea.errors, err)
    }
}

func (ea *ErrorAggregator) HasErrors() bool {
    return len(ea.errors) > 0
}

func (ea *ErrorAggregator) Error() string {
    if len(ea.errors) == 0 {
        return "没有错误"
    }
    
    var messages []string
    for i, err := range ea.errors {
        messages = append(messages, fmt.Sprintf("%d. %v", i+1, err))
    }
    
    return fmt.Sprintf("发现 %d 个错误:\n%s", 
        len(ea.errors), strings.Join(messages, "\n"))
}

func (ea *ErrorAggregator) Errors() []error {
    return ea.errors
}

func demonstrateErrorAggregation() {
    fmt.Println("\n=== 错误聚合 ===")
    
    // 批量处理多个操作,收集所有错误
    aggregator := NewErrorAggregator()
    
    operations := []struct {
        name string
        fn   func() error
    }{
        {"操作1", func() error { return validateInput("valid") }},
        {"操作2", func() error { return validateInput("") }},
        {"操作3", func() error { return validateInput("invalid") }},
        {"操作4", func() error { return validateInput("valid2") }},
    }
    
    fmt.Println("执行批量操作:")
    for _, op := range operations {
        if err := op.fn(); err != nil {
            fmt.Printf("  %s 失败: %v\n", op.name, err)
            aggregator.Add(fmt.Errorf("%s: %w", op.name, err))
        } else {
            fmt.Printf("  %s 成功\n", op.name)
        }
    }
    
    if aggregator.HasErrors() {
        fmt.Printf("\n批量操作结果:\n%v\n", aggregator)
    } else {
        fmt.Printf("\n所有操作都成功\n")
    }
}

func validateInput(input string) error {
    if input == "" {
        return fmt.Errorf("输入不能为空")
    }
    if input == "invalid" {
        return fmt.Errorf("输入格式无效")
    }
    return nil
}

func demonstrateErrorTransformation() {
    fmt.Println("\n=== 错误转换 ===")
    
    // 演示将底层错误转换为业务错误
    if err := businessOperation("user123"); err != nil {
        fmt.Printf("业务操作失败: %v\n", err)
        
        // 根据错误类型进行不同处理
        if busErr, ok := err.(*BusinessError); ok {
            fmt.Printf("错误代码: %s\n", busErr.Code)
            fmt.Printf("用户消息: %s\n", busErr.UserMessage)
        }
    }
}

type BusinessError struct {
    Code        string
    Message     string
    UserMessage string
    Cause       error
}

func (e *BusinessError) Error() string {
    return fmt.Sprintf("[%s] %s", e.Code, e.Message)
}

func (e *BusinessError) Unwrap() error {
    return e.Cause
}

func businessOperation(userID string) error {
    // 模拟底层操作
    if err := databaseQuery(userID); err != nil {
        // 转换为业务错误
        return &BusinessError{
            Code:        "USER_NOT_FOUND",
            Message:     fmt.Sprintf("用户查询失败: %v", err),
            UserMessage: "用户不存在或已被删除",
            Cause:       err,
        }
    }
    return nil
}

func databaseQuery(userID string) error {
    if userID == "user123" {
        return fmt.Errorf("数据库连接超时")
    }
    return nil
}

func demonstrateErrorContext() {
    fmt.Println("\n=== 错误上下文 ===")
    
    // 演示为错误添加上下文信息
    ctx := &OperationContext{
        UserID:    "user456",
        RequestID: "req-789",
        Operation: "UpdateProfile",
    }
    
    if err := performOperation(ctx); err != nil {
        fmt.Printf("操作失败: %v\n", err)
    }
}

type OperationContext struct {
    UserID    string
    RequestID string
    Operation string
}

type ContextualError struct {
    Context *OperationContext
    Err     error
}

func (e *ContextualError) Error() string {
    return fmt.Sprintf("操作失败 [用户:%s, 请求:%s, 操作:%s]: %v",
        e.Context.UserID, e.Context.RequestID, e.Context.Operation, e.Err)
}

func (e *ContextualError) Unwrap() error {
    return e.Err
}

func performOperation(ctx *OperationContext) error {
    if err := validateOperation(ctx); err != nil {
        return &ContextualError{
            Context: ctx,
            Err:     err,
        }
    }
    return nil
}

func validateOperation(ctx *OperationContext) error {
    if ctx.UserID == "user456" {
        return fmt.Errorf("用户权限不足")
    }
    return nil
}

func demonstrateErrorClassification() {
    fmt.Println("\n=== 错误分类处理 ===")
    
    errors := []error{
        &NetworkError{Message: "连接超时", Retryable: true},
        &ValidationError{Field: "email", Message: "格式无效"},
        &AuthError{Message: "认证失败", Code: "INVALID_TOKEN"},
        &SystemError{Message: "磁盘空间不足", Critical: true},
    }
    
    for i, err := range errors {
        fmt.Printf("\n错误 %d: %v\n", i+1, err)
        handleClassifiedError(err)
    }
}

// 错误分类接口
type RetryableError interface {
    error
    IsRetryable() bool
}

type CriticalError interface {
    error
    IsCritical() bool
}

// 网络错误
type NetworkError struct {
    Message   string
    Retryable bool
}

func (e *NetworkError) Error() string {
    return fmt.Sprintf("网络错误: %s", e.Message)
}

func (e *NetworkError) IsRetryable() bool {
    return e.Retryable
}

// 验证错误
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("验证错误 [%s]: %s", e.Field, e.Message)
}

// 认证错误
type AuthError struct {
    Message string
    Code    string
}

func (e *AuthError) Error() string {
    return fmt.Sprintf("认证错误 [%s]: %s", e.Code, e.Message)
}

// 系统错误
type SystemError struct {
    Message  string
    Critical bool
}

func (e *SystemError) Error() string {
    return fmt.Sprintf("系统错误: %s", e.Message)
}

func (e *SystemError) IsCritical() bool {
    return e.Critical
}

func handleClassifiedError(err error) {
    // 检查是否可重试
    if retryable, ok := err.(RetryableError); ok && retryable.IsRetryable() {
        fmt.Printf("  处理策略: 可重试错误,安排重试\n")
    }
    
    // 检查是否为关键错误
    if critical, ok := err.(CriticalError); ok && critical.IsCritical() {
        fmt.Printf("  处理策略: 关键错误,立即通知管理员\n")
    }
    
    // 根据具体类型处理
    switch e := err.(type) {
    case *NetworkError:
        fmt.Printf("  处理策略: 网络错误,检查网络连接\n")
    case *ValidationError:
        fmt.Printf("  处理策略: 验证错误,返回用户友好消息\n")
    case *AuthError:
        fmt.Printf("  处理策略: 认证错误,重定向到登录页面\n")
    case *SystemError:
        fmt.Printf("  处理策略: 系统错误,记录日志并监控\n")
    default:
        fmt.Printf("  处理策略: 未知错误类型,使用默认处理\n")
    }
}
go run error_patterns.go

总结

通过本章学习,你已经掌握了Go语言错误处理的核心概念和最佳实践:

关键要点

  1. 错误处理哲学

    • 错误是值,不是异常
    • 显式错误检查
    • 简单、明确、可预测
  2. error接口

    • 简单的Error() string方法
    • 自定义错误类型
    • 错误包装和链式错误
  3. 最佳实践

    • 总是检查错误
    • 提供有意义的错误信息
    • 选择合适的错误处理策略
    • 正确传播错误
  4. panic和recover

    • 仅在无法恢复的情况下使用panic
    • 使用recover进行资源清理
    • 提供panic和non-panic版本的API
  5. 错误处理模式

    • 错误聚合
    • 错误转换
    • 错误上下文
    • 错误分类

下一步

下一章我们将学习Go语言的测试和调试,包括单元测试、基准测试、调试技巧和性能分析。这些技能将帮助你编写更可靠、更高性能的Go程序。