Files
golang/golang-learning/07-error-handling/01-basic-errors.go
2025-08-24 11:24:52 +08:00

958 lines
24 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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()
*/