/* 01-basic-errors.go - Go 语言基础错误处理详解 学习目标: 1. 理解 Go 语言的错误处理哲学 2. 掌握 error 接口的使用 3. 学会基本的错误处理模式 4. 了解错误传播和包装 5. 掌握错误处理的最佳实践 知识点: - error 接口的定义和实现 - 错误的创建和返回 - 错误检查和处理 - 错误传播和包装 - 错误处理的惯用法 - 错误信息的设计 */ package main import ( "crypto/rand" "encoding/json" "errors" "fmt" "os" "strings" "sync" "time" ) func main() { fmt.Println("=== Go 语言基础错误处理详解 ===\\n") // 演示错误接口的基本概念 demonstrateErrorInterface() // 演示错误的创建方式 demonstrateErrorCreation() // 演示错误检查和处理 demonstrateErrorHandling() // 演示错误传播 demonstrateErrorPropagation() // 演示错误包装 demonstrateErrorWrapping() // 演示错误处理模式 demonstrateErrorPatterns() // 演示实际应用场景 demonstratePracticalExamples() } // demonstrateErrorInterface 演示错误接口的基本概念 func demonstrateErrorInterface() { fmt.Println("1. 错误接口的基本概念:") // error 接口的定义 fmt.Printf(" error 接口的定义:\\n") fmt.Printf(" type error interface {\\n") fmt.Printf(" Error() string\\n") fmt.Printf(" }\\n") fmt.Printf("\\n") fmt.Printf(" 错误处理的特点:\\n") fmt.Printf(" - 错误是值,可以像其他值一样传递\\n") fmt.Printf(" - 使用多返回值模式处理错误\\n") fmt.Printf(" - 显式错误检查,不隐藏错误\\n") fmt.Printf(" - 错误应该被处理,不应该被忽略\\n") fmt.Printf(" - 错误信息应该清晰、有用\\n") // 基本错误示例 fmt.Printf(" 基本错误示例:\\n") // 成功的情况 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) } // nil 错误检查 fmt.Printf(" nil 错误检查:\\n") var nilError error fmt.Printf(" nil 错误: %v\\n", nilError) fmt.Printf(" nil 错误 == nil: %t\\n", nilError == nil) // 错误比较 fmt.Printf(" 错误比较:\\n") err1 := errors.New("相同的错误") err2 := errors.New("相同的错误") fmt.Printf(" err1 == err2: %t (内容相同但不是同一个对象)\\n", err1 == err2) fmt.Printf(" err1.Error() == err2.Error(): %t\\n", err1.Error() == err2.Error()) fmt.Println() } // demonstrateErrorCreation 演示错误的创建方式 func demonstrateErrorCreation() { fmt.Println("2. 错误的创建方式:") // 使用 errors.New 创建错误 fmt.Printf(" 使用 errors.New 创建错误:\\n") err1 := errors.New("这是一个简单的错误") fmt.Printf(" 错误1: %v\\n", err1) // 使用 fmt.Errorf 创建格式化错误 fmt.Printf(" 使用 fmt.Errorf 创建格式化错误:\\n") username := "alice" err2 := fmt.Errorf("用户 %s 不存在", username) fmt.Printf(" 错误2: %v\\n", err2) // 创建带有更多信息的错误 age := -5 err3 := fmt.Errorf("无效的年龄值: %d (年龄必须大于0)", age) fmt.Printf(" 错误3: %v\\n", err3) // 使用预定义错误 fmt.Printf(" 使用预定义错误:\\n") predefinedErrors := []error{ ErrInvalidInput, ErrNotFound, ErrPermissionDenied, ErrTimeout, } for i, err := range predefinedErrors { fmt.Printf(" 预定义错误%d: %v\\n", i+1, err) } // 条件性错误创建 fmt.Printf(" 条件性错误创建:\\n") testValues := []int{-1, 0, 5, 101} for _, value := range testValues { if err := validateAge(value); err != nil { fmt.Printf(" 验证年龄 %d: %v\\n", value, err) } else { fmt.Printf(" 验证年龄 %d: 有效\\n", value) } } fmt.Println() } // demonstrateErrorHandling 演示错误检查和处理 func demonstrateErrorHandling() { fmt.Println("3. 错误检查和处理:") // 基本错误检查模式 fmt.Printf(" 基本错误检查模式:\\n") // 立即检查错误 content, err := readFile("example.txt") if err != nil { fmt.Printf(" 读取文件失败: %v\\n", err) } else { fmt.Printf(" 文件内容: %s\\n", content) } // 多个操作的错误处理 fmt.Printf(" 多个操作的错误处理:\\n") err = performMultipleOperations() if err != nil { fmt.Printf(" 多个操作失败: %v\\n", err) } else { fmt.Printf(" 所有操作成功完成\\n") } // 错误类型检查 fmt.Printf(" 错误类型检查:\\n") testOperations := []func() error{ func() error { return ErrNotFound }, func() error { return ErrPermissionDenied }, func() error { return fmt.Errorf("未知错误") }, func() error { return nil }, } for i, op := range testOperations { err := op() fmt.Printf(" 操作%d: ", i+1) handleSpecificError(err) } // 错误恢复 fmt.Printf(" 错误恢复:\\n") data, err := fetchDataWithRetry("https://api.example.com/data", 3) if err != nil { fmt.Printf(" 获取数据失败: %v\\n", err) } else { fmt.Printf(" 获取数据成功: %s\\n", data) } // 部分失败处理 fmt.Printf(" 部分失败处理:\\n") results, errors := processBatch([]string{"item1", "item2", "invalid", "item4"}) fmt.Printf(" 成功处理: %v\\n", results) fmt.Printf(" 处理错误: %v\\n", errors) fmt.Println() } // demonstrateErrorPropagation 演示错误传播 func demonstrateErrorPropagation() { fmt.Println("4. 错误传播:") // 简单错误传播 fmt.Printf(" 简单错误传播:\\n") result, err := calculateTotal([]float64{10.5, 20.0, -5.0, 15.5}) if err != nil { fmt.Printf(" 计算总和失败: %v\\n", err) } else { fmt.Printf(" 总和: %.2f\\n", result) } // 带上下文的错误传播 fmt.Printf(" 带上下文的错误传播:\\n") user, err := getUserProfile("nonexistent_user") if err != nil { fmt.Printf(" 获取用户资料失败: %v\\n", err) } else { fmt.Printf(" 用户资料: %+v\\n", user) } // 错误链 fmt.Printf(" 错误链:\\n") err = processOrder("invalid_order_id") if err != nil { fmt.Printf(" 处理订单失败: %v\\n", err) // 展示错误链 fmt.Printf(" 错误链分析:\\n") printErrorChain(err, " ") } // 选择性错误传播 fmt.Printf(" 选择性错误传播:\\n") results := []string{} errors := []error{} items := []string{"valid1", "invalid", "valid2", "error", "valid3"} for _, item := range items { result, err := processItem(item) if err != nil { errors = append(errors, fmt.Errorf("处理 %s 失败: %w", item, err)) } else { results = append(results, result) } } fmt.Printf(" 成功结果: %v\\n", results) if len(errors) > 0 { fmt.Printf(" 错误列表:\\n") for _, err := range errors { fmt.Printf(" - %v\\n", err) } } fmt.Println() } // demonstrateErrorWrapping 演示错误包装 func demonstrateErrorWrapping() { fmt.Println("5. 错误包装:") // 基本错误包装 fmt.Printf(" 基本错误包装:\\n") originalErr := errors.New("原始错误") wrappedErr := fmt.Errorf("包装错误: %w", originalErr) fmt.Printf(" 原始错误: %v\\n", originalErr) fmt.Printf(" 包装错误: %v\\n", wrappedErr) // 错误解包 fmt.Printf(" 错误解包:\\n") unwrappedErr := errors.Unwrap(wrappedErr) fmt.Printf(" 解包后的错误: %v\\n", unwrappedErr) fmt.Printf(" 解包后是否为原始错误: %t\\n", unwrappedErr == originalErr) // 错误检查 (errors.Is) fmt.Printf(" 错误检查 (errors.Is):\\n") fmt.Printf(" wrappedErr 是否为 originalErr: %t\\n", errors.Is(wrappedErr, originalErr)) fmt.Printf(" wrappedErr 是否为 ErrNotFound: %t\\n", errors.Is(wrappedErr, ErrNotFound)) // 多层包装 fmt.Printf(" 多层包装:\\n") baseErr := ErrNotFound level1Err := fmt.Errorf("数据库查询失败: %w", baseErr) level2Err := fmt.Errorf("用户服务错误: %w", level1Err) level3Err := fmt.Errorf("API 请求失败: %w", level2Err) fmt.Printf(" 多层包装错误: %v\\n", level3Err) fmt.Printf(" 是否包含 ErrNotFound: %t\\n", errors.Is(level3Err, ErrNotFound)) // 错误类型断言 (errors.As) fmt.Printf(" 错误类型断言 (errors.As):\\n") customErr := &CustomError{ Code: 404, Message: "资源未找到", Details: "请求的用户ID不存在", } wrappedCustomErr := fmt.Errorf("处理请求失败: %w", customErr) var targetErr *CustomError if errors.As(wrappedCustomErr, &targetErr) { fmt.Printf(" 找到自定义错误: Code=%d, Message=%s\\n", targetErr.Code, targetErr.Message) } fmt.Println() } // demonstrateErrorPatterns 演示错误处理模式 func demonstrateErrorPatterns() { fmt.Println("6. 错误处理模式:") // 哨兵错误模式 fmt.Printf(" 哨兵错误模式:\\n") _, err := findUser("nonexistent") if errors.Is(err, ErrUserNotFound) { fmt.Printf(" 用户不存在,使用默认用户\\n") } else if err != nil { fmt.Printf(" 查找用户时发生其他错误: %v\\n", err) } // 错误类型模式 fmt.Printf(" 错误类型模式:\\n") err = performNetworkOperation() var netErr *NetworkError if errors.As(err, &netErr) { if netErr.Temporary { fmt.Printf(" 临时网络错误,可以重试: %v\\n", netErr) } else { fmt.Printf(" 永久网络错误: %v\\n", netErr) } } // 不透明错误模式 fmt.Printf(" 不透明错误模式:\\n") err = callExternalAPI() if err != nil { fmt.Printf(" 外部API调用失败: %v\\n", err) // 不检查具体错误类型,统一处理 } // 错误聚合模式 fmt.Printf(" 错误聚合模式:\\n") validator := NewValidator() validator.ValidateRequired("name", "") validator.ValidateEmail("email", "invalid-email") validator.ValidateRange("age", -5, 0, 120) if validator.HasErrors() { fmt.Printf(" 验证失败:\\n") for _, err := range validator.Errors() { fmt.Printf(" - %v\\n", err) } } // 错误恢复模式 fmt.Printf(" 错误恢复模式:\\n") result := performWithFallback() fmt.Printf(" 操作结果: %s\\n", result) // 错误重试模式 fmt.Printf(" 错误重试模式:\\n") data, err := retryOperation(func() (string, error) { // 模拟不稳定的操作 if len(fmt.Sprintf("%d", rand.Int()))%3 == 0 { return "成功数据", nil } return "", errors.New("临时失败") }, 3) if err != nil { fmt.Printf(" 重试操作最终失败: %v\\n", err) } else { fmt.Printf(" 重试操作成功: %s\\n", data) } fmt.Println() } // demonstratePracticalExamples 演示实际应用场景 func demonstratePracticalExamples() { fmt.Println("7. 实际应用场景:") // 文件操作错误处理 fmt.Printf(" 文件操作错误处理:\\n") content, err := safeReadFile("config.json") if err != nil { fmt.Printf(" 读取配置文件失败: %v\\n", err) } else { fmt.Printf(" 配置内容: %s\\n", content) } // 网络请求错误处理 fmt.Printf(" 网络请求错误处理:\\n") response, err := httpGet("https://api.example.com/users/1") if err != nil { fmt.Printf(" HTTP请求失败: %v\\n", err) } else { fmt.Printf(" HTTP响应: %s\\n", response) } // 数据库操作错误处理 fmt.Printf(" 数据库操作错误处理:\\n") user := User{ID: 1, Name: "Alice", Email: "alice@example.com"} err = saveUser(user) if err != nil { fmt.Printf(" 保存用户失败: %v\\n", err) } else { fmt.Printf(" 用户保存成功\\n") } // JSON 解析错误处理 fmt.Printf(" JSON 解析错误处理:\\n") jsonData := `{"name": "Bob", "age": "invalid"}` user, err = parseUser(jsonData) if err != nil { fmt.Printf(" JSON解析失败: %v\\n", err) } else { fmt.Printf(" 解析用户: %+v\\n", user) } // 业务逻辑错误处理 fmt.Printf(" 业务逻辑错误处理:\\n") account := BankAccount{Balance: 100.0} err = account.Withdraw(150.0) if err != nil { fmt.Printf(" 取款失败: %v\\n", err) } else { fmt.Printf(" 取款成功,余额: %.2f\\n", account.Balance) } // 并发错误处理 fmt.Printf(" 并发错误处理:\\n") results, errors := processConcurrently([]string{"task1", "task2", "error_task", "task4"}) fmt.Printf(" 并发处理结果: %v\\n", results) if len(errors) > 0 { fmt.Printf(" 并发处理错误:\\n") for i, err := range errors { if err != nil { fmt.Printf(" 任务%d错误: %v\\n", i, err) } } } fmt.Println() } // ========== 类型定义和辅助函数 ========== // 预定义错误 var ( ErrInvalidInput = errors.New("输入无效") ErrNotFound = errors.New("未找到") ErrPermissionDenied = errors.New("权限被拒绝") ErrTimeout = errors.New("操作超时") ErrUserNotFound = errors.New("用户未找到") ) // CustomError 自定义错误类型 type CustomError struct { Code int Message string Details string } func (e *CustomError) Error() string { return fmt.Sprintf("错误 %d: %s (%s)", e.Code, e.Message, e.Details) } // NetworkError 网络错误类型 type NetworkError struct { Op string URL string Temporary bool Err error } func (e *NetworkError) Error() string { return fmt.Sprintf("网络错误 %s %s: %v", e.Op, e.URL, e.Err) } // User 用户结构 type User struct { ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` Age int `json:"age"` } // BankAccount 银行账户 type BankAccount struct { Balance float64 } func (ba *BankAccount) Withdraw(amount float64) error { if amount <= 0 { return fmt.Errorf("取款金额必须大于0,实际: %.2f", amount) } if amount > ba.Balance { return fmt.Errorf("余额不足: 需要 %.2f,可用 %.2f", amount, ba.Balance) } ba.Balance -= amount return nil } // Validator 验证器 type Validator struct { errors []error } func NewValidator() *Validator { return &Validator{errors: make([]error, 0)} } func (v *Validator) ValidateRequired(field, value string) { if value == "" { v.errors = append(v.errors, fmt.Errorf("字段 %s 是必需的", field)) } } func (v *Validator) ValidateEmail(field, value string) { if value != "" && !strings.Contains(value, "@") { v.errors = append(v.errors, fmt.Errorf("字段 %s 不是有效的邮箱地址", field)) } } func (v *Validator) ValidateRange(field string, value, min, max int) { if value < min || value > max { v.errors = append(v.errors, fmt.Errorf("字段 %s 必须在 %d 到 %d 之间,实际: %d", field, min, max, value)) } } func (v *Validator) HasErrors() bool { return len(v.errors) > 0 } func (v *Validator) Errors() []error { return v.errors } // ========== 辅助函数 ========== // divide 除法运算 func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("除数不能为零") } return a / b, nil } // validateAge 验证年龄 func validateAge(age int) error { if age < 0 { return fmt.Errorf("年龄不能为负数: %d", age) } if age > 150 { return fmt.Errorf("年龄不能超过150: %d", age) } return nil } // readFile 读取文件 func readFile(filename string) (string, error) { // 模拟文件读取 if filename == "example.txt" { return "", fmt.Errorf("文件 %s 不存在", filename) } return "文件内容", nil } // performMultipleOperations 执行多个操作 func performMultipleOperations() error { // 模拟多个操作 operations := []func() error{ func() error { return nil }, // 成功 func() error { return nil }, // 成功 func() error { return errors.New("第三个操作失败") }, // 失败 } for i, op := range operations { if err := op(); err != nil { return fmt.Errorf("操作 %d 失败: %w", i+1, err) } } return nil } // handleSpecificError 处理特定错误 func handleSpecificError(err error) { if err == nil { fmt.Printf("成功\\n") return } switch { case errors.Is(err, ErrNotFound): fmt.Printf("资源未找到\\n") case errors.Is(err, ErrPermissionDenied): fmt.Printf("权限不足\\n") default: fmt.Printf("未知错误: %v\\n", err) } } // fetchDataWithRetry 带重试的数据获取 func fetchDataWithRetry(url string, maxRetries int) (string, error) { var lastErr error for i := 0; i < maxRetries; i++ { // 模拟网络请求 if rand.Float32() < 0.3 { // 30% 成功率 return fmt.Sprintf("数据来自 %s", url), nil } lastErr = fmt.Errorf("请求 %s 失败 (尝试 %d/%d)", url, i+1, maxRetries) time.Sleep(100 * time.Millisecond) // 重试延迟 } return "", fmt.Errorf("重试 %d 次后仍然失败: %w", maxRetries, lastErr) } // processBatch 批量处理 func processBatch(items []string) ([]string, []error) { var results []string var errors []error for _, item := range items { if item == "invalid" { errors = append(errors, fmt.Errorf("无效项目: %s", item)) } else { results = append(results, fmt.Sprintf("处理后的%s", item)) } } return results, errors } // calculateTotal 计算总和 func calculateTotal(values []float64) (float64, error) { total := 0.0 for i, value := range values { if value < 0 { return 0, fmt.Errorf("索引 %d 的值不能为负数: %.2f", i, value) } total += value } return total, nil } // getUserProfile 获取用户资料 func getUserProfile(username string) (*User, error) { // 模拟数据库查询 if username == "nonexistent_user" { return nil, fmt.Errorf("获取用户资料失败: %w", ErrUserNotFound) } return &User{ ID: 1, Name: username, Email: username + "@example.com", }, nil } // processOrder 处理订单 func processOrder(orderID string) error { // 模拟订单处理链 if err := validateOrder(orderID); err != nil { return fmt.Errorf("处理订单 %s 失败: %w", orderID, err) } return nil } // validateOrder 验证订单 func validateOrder(orderID string) error { if orderID == "invalid_order_id" { if err := checkOrderExists(orderID); err != nil { return fmt.Errorf("订单验证失败: %w", err) } } return nil } // checkOrderExists 检查订单是否存在 func checkOrderExists(orderID string) error { return fmt.Errorf("数据库查询失败: %w", ErrNotFound) } // printErrorChain 打印错误链 func printErrorChain(err error, indent string) { if err == nil { return } fmt.Printf("%s- %v\\n", indent, err) if unwrapped := errors.Unwrap(err); unwrapped != nil { printErrorChain(unwrapped, indent+" ") } } // processItem 处理项目 func processItem(item string) (string, error) { switch item { case "invalid": return "", ErrInvalidInput case "error": return "", errors.New("处理错误") default: return fmt.Sprintf("处理后的%s", item), nil } } // findUser 查找用户 func findUser(username string) (*User, error) { if username == "nonexistent" { return nil, ErrUserNotFound } return &User{Name: username}, nil } // performNetworkOperation 执行网络操作 func performNetworkOperation() error { return &NetworkError{ Op: "GET", URL: "https://api.example.com", Temporary: true, Err: errors.New("连接超时"), } } // callExternalAPI 调用外部API func callExternalAPI() error { return errors.New("外部服务不可用") } // performWithFallback 带回退的操作 func performWithFallback() string { // 尝试主要操作 if err := primaryOperation(); err != nil { // 主要操作失败,使用回退 return fallbackOperation() } return "主要操作成功" } // primaryOperation 主要操作 func primaryOperation() error { return errors.New("主要操作失败") } // fallbackOperation 回退操作 func fallbackOperation() string { return "回退操作成功" } // retryOperation 重试操作 func retryOperation(op func() (string, error), maxRetries int) (string, error) { var lastErr error for i := 0; i < maxRetries; i++ { result, err := op() if err == nil { return result, nil } lastErr = err fmt.Printf(" 尝试 %d/%d 失败: %v\\n", i+1, maxRetries, err) if i < maxRetries-1 { time.Sleep(100 * time.Millisecond) } } return "", fmt.Errorf("重试 %d 次后失败: %w", maxRetries, lastErr) } // safeReadFile 安全读取文件 func safeReadFile(filename string) (string, error) { // 模拟文件读取 switch filename { case "config.json": return "", fmt.Errorf("读取文件 %s 失败: %w", filename, os.ErrNotExist) default: return "文件内容", nil } } // httpGet HTTP GET 请求 func httpGet(url string) (string, error) { // 模拟HTTP请求 if strings.Contains(url, "example.com") { return "", &NetworkError{ Op: "GET", URL: url, Temporary: false, Err: errors.New("域名解析失败"), } } return "HTTP响应内容", nil } // saveUser 保存用户 func saveUser(user User) error { // 模拟数据库保存 if user.Email == "" { return fmt.Errorf("保存用户失败: %w", ErrInvalidInput) } return nil } // parseUser 解析用户JSON func parseUser(jsonData string) (User, error) { var user User if err := json.Unmarshal([]byte(jsonData), &user); err != nil { return User{}, fmt.Errorf("解析用户JSON失败: %w", err) } return user, nil } // processConcurrently 并发处理 func processConcurrently(tasks []string) ([]string, []error) { var wg sync.WaitGroup results := make([]string, len(tasks)) errors := make([]error, len(tasks)) for i, task := range tasks { wg.Add(1) go func(index int, taskName string) { defer wg.Done() // 模拟任务处理 if strings.Contains(taskName, "error") { errors[index] = fmt.Errorf("任务 %s 处理失败", taskName) } else { results[index] = fmt.Sprintf("处理后的%s", taskName) } }(i, task) } wg.Wait() return results, errors } /* 运行这个程序: go run 01-basic-errors.go 学习要点: 1. Go 语言使用显式错误处理,错误是值 2. error 接口只有一个 Error() string 方法 3. 使用多返回值模式处理错误 4. 错误应该被检查和处理,不应该被忽略 5. 错误信息应该清晰、有用、包含上下文 错误处理的特点: 1. 显式性:错误必须被显式检查 2. 简单性:error 接口设计简单 3. 组合性:错误可以被包装和组合 4. 传播性:错误可以在调用栈中传播 5. 灵活性:支持多种错误处理模式 错误创建方式: 1. errors.New():创建简单错误 2. fmt.Errorf():创建格式化错误 3. 自定义错误类型:实现 error 接口 4. 预定义错误:定义常用错误变量 5. 错误包装:使用 %w 动词包装错误 错误检查模式: 1. 立即检查:if err != nil { ... } 2. 错误传播:return err 或 return fmt.Errorf("...: %w", err) 3. 错误类型检查:errors.Is() 和 errors.As() 4. 错误恢复:提供默认值或回退方案 5. 错误聚合:收集多个错误 错误处理模式: 1. 哨兵错误:预定义的错误值 2. 错误类型:自定义错误结构体 3. 不透明错误:不检查具体错误类型 4. 错误包装:添加上下文信息 5. 错误聚合:收集和批量处理错误 最佳实践: 1. 错误信息应该小写开头,不以标点结尾 2. 错误信息应该包含足够的上下文 3. 使用错误包装传播上下文 4. 不要忽略错误,至少要记录日志 5. 在适当的层级处理错误 常见陷阱: 1. 忽略错误返回值 2. 错误信息不够清晰 3. 过度包装错误 4. 不适当的错误类型检查 5. 在错误处理中引入新的错误 性能考虑: 1. 错误创建有一定开销 2. 错误包装会增加内存使用 3. 错误检查是必要的开销 4. 避免在热点路径创建复杂错误 5. 合理使用预定义错误 注意事项: 1. nil 错误表示没有错误 2. 错误比较使用 == 或 errors.Is() 3. 错误类型断言使用 errors.As() 4. 错误包装使用 fmt.Errorf() 和 %w 5. 错误解包使用 errors.Unwrap() */