This commit is contained in:
2025-08-24 11:24:52 +08:00
parent ec23aa5c26
commit 61e51ad014
12 changed files with 11554 additions and 4 deletions

View File

@@ -0,0 +1,957 @@
/*
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()
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,941 @@
/*
03-panic-recover.go - Go 语言 Panic 和 Recover 详解
学习目标:
1. 理解 panic 的概念和使用场景
2. 掌握 recover 的恢复机制
3. 学会 defer 与 panic/recover 的配合
4. 了解 panic 的传播机制
5. 掌握 panic/recover 的最佳实践
知识点:
- panic 的触发条件和行为
- recover 的恢复机制
- defer 语句的执行顺序
- panic 的传播和栈展开
- panic/recover 的实际应用
- 错误处理 vs panic 的选择
*/
package main
import (
"fmt"
"runtime"
"strconv"
"time"
)
func main() {
fmt.Println("=== Go 语言 Panic 和 Recover 详解 ===\\n")
// 演示 panic 的基本概念
demonstratePanicBasics()
// 演示 recover 的使用
demonstrateRecover()
// 演示 defer 与 panic/recover 的配合
demonstrateDeferWithPanicRecover()
// 演示 panic 的传播
demonstratePanicPropagation()
// 演示 panic/recover 的实际应用
demonstratePracticalUsage()
// 演示最佳实践
demonstrateBestPractices()
// 演示错误处理 vs panic 的选择
demonstrateErrorVsPanic()
}
// demonstratePanicBasics 演示 panic 的基本概念
func demonstratePanicBasics() {
fmt.Println("1. Panic 的基本概念:")
// panic 的基本特性
fmt.Printf(" Panic 的基本特性:\\n")
fmt.Printf(" - panic 是运行时错误,会导致程序崩溃\\n")
fmt.Printf(" - panic 会停止当前函数的执行\\n")
fmt.Printf(" - panic 会执行当前函数的 defer 语句\\n")
fmt.Printf(" - panic 会向上传播到调用栈\\n")
fmt.Printf(" - panic 可以被 recover 捕获\\n")
// 手动触发 panic
fmt.Printf(" 手动触发 panic:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 捕获到 panic: %v\\n", r)
}
}()
fmt.Printf(" 准备触发 panic\\n")
panic("这是一个手动触发的 panic")
fmt.Printf(" 这行代码不会执行\\n")
}()
// 常见的 panic 场景
fmt.Printf(" 常见的 panic 场景:\\n")
// 1. 数组越界
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 数组越界 panic: %v\\n", r)
}
}()
arr := [3]int{1, 2, 3}
fmt.Printf(" 访问数组索引 5: %d\\n", arr[5]) // 会 panic
}()
// 2. 空指针解引用
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 空指针解引用 panic: %v\\n", r)
}
}()
var ptr *int
fmt.Printf(" 解引用空指针: %d\\n", *ptr) // 会 panic
}()
// 3. 类型断言失败
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 类型断言失败 panic: %v\\n", r)
}
}()
var x interface{} = "hello"
num := x.(int) // 会 panic
fmt.Printf(" 类型断言结果: %d\\n", num)
}()
// 4. 向已关闭的 channel 发送数据
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 向已关闭 channel 发送数据 panic: %v\\n", r)
}
}()
ch := make(chan int)
close(ch)
ch <- 1 // 会 panic
}()
fmt.Println()
}
// demonstrateRecover 演示 recover 的使用
func demonstrateRecover() {
fmt.Println("2. Recover 的使用:")
// recover 的基本概念
fmt.Printf(" Recover 的基本概念:\\n")
fmt.Printf(" - recover 只能在 defer 函数中使用\\n")
fmt.Printf(" - recover 可以捕获当前 goroutine 的 panic\\n")
fmt.Printf(" - recover 返回 panic 的值\\n")
fmt.Printf(" - recover 后程序可以继续执行\\n")
// 基本 recover 示例
fmt.Printf(" 基本 recover 示例:\\n")
result := safeFunction(func() int {
panic("计算过程中发生错误")
return 42
})
fmt.Printf(" 安全函数返回: %d\\n", result)
// recover 的返回值
fmt.Printf(" recover 的返回值:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" panic 值: %v\\n", r)
fmt.Printf(" panic 类型: %T\\n", r)
// 根据 panic 类型进行不同处理
switch v := r.(type) {
case string:
fmt.Printf(" 字符串 panic: %s\\n", v)
case int:
fmt.Printf(" 整数 panic: %d\\n", v)
case error:
fmt.Printf(" 错误 panic: %v\\n", v)
default:
fmt.Printf(" 其他类型 panic: %v\\n", v)
}
}
}()
// 触发不同类型的 panic
testPanics := []interface{}{
"字符串错误",
42,
fmt.Errorf("错误对象"),
struct{ Message string }{"结构体错误"},
}
for i, panicValue := range testPanics {
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 测试 %d - panic: %v (%T)\\n", i+1, r, r)
}
}()
panic(panicValue)
}()
}
}()
// recover 的条件
fmt.Printf(" recover 的条件:\\n")
// 正确的 recover 使用
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 正确捕获 panic: %v\\n", r)
}
}()
panic("正确的 recover 示例")
}()
// 错误的 recover 使用
func() {
defer func() {
// 这个 recover 不会捕获到 panic因为不是直接在 defer 中调用
go func() {
if r := recover(); r != nil {
fmt.Printf(" 错误的 recover: %v\\n", r)
}
}()
}()
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 正确捕获嵌套 panic: %v\\n", r)
}
}()
panic("嵌套 panic 示例")
}()
fmt.Println()
}
// demonstrateDeferWithPanicRecover 演示 defer 与 panic/recover 的配合
func demonstrateDeferWithPanicRecover() {
fmt.Println("3. Defer 与 Panic/Recover 的配合:")
// defer 的执行顺序
fmt.Printf(" defer 的执行顺序:\\n")
func() {
defer fmt.Printf(" defer 1\\n")
defer fmt.Printf(" defer 2\\n")
defer func() {
if r := recover(); r != nil {
fmt.Printf(" recover: %v\\n", r)
}
}()
defer fmt.Printf(" defer 3\\n")
fmt.Printf(" 正常执行\\n")
panic("测试 defer 顺序")
fmt.Printf(" 这行不会执行\\n")
}()
// 多层 defer 和 recover
fmt.Printf(" 多层 defer 和 recover:\\n")
func() {
defer func() {
fmt.Printf(" 外层 defer 开始\\n")
if r := recover(); r != nil {
fmt.Printf(" 外层 recover: %v\\n", r)
}
fmt.Printf(" 外层 defer 结束\\n")
}()
defer func() {
fmt.Printf(" 内层 defer 开始\\n")
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 内层 recover: %v\\n", r)
}
}()
fmt.Printf(" 内层 defer 结束\\n")
}()
panic("多层 defer 测试")
}()
// defer 中的资源清理
fmt.Printf(" defer 中的资源清理:\\n")
processWithCleanup := func() {
// 模拟资源分配
resource := "重要资源"
fmt.Printf(" 分配资源: %s\\n", resource)
defer func() {
// 资源清理
fmt.Printf(" 清理资源: %s\\n", resource)
// 捕获 panic
if r := recover(); r != nil {
fmt.Printf(" 处理过程中发生 panic: %v\\n", r)
fmt.Printf(" 资源已安全清理\\n")
}
}()
// 模拟可能 panic 的操作
if time.Now().UnixNano()%2 == 0 {
panic("随机 panic")
}
fmt.Printf(" 正常处理完成\\n")
}
// 执行多次以演示不同情况
for i := 0; i < 3; i++ {
fmt.Printf(" 执行 %d:\\n", i+1)
processWithCleanup()
}
fmt.Println()
}
// demonstratePanicPropagation 演示 panic 的传播
func demonstratePanicPropagation() {
fmt.Println("4. Panic 的传播:")
// panic 的栈展开
fmt.Printf(" panic 的栈展开:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 最终捕获 panic: %v\\n", r)
// 打印调用栈
buf := make([]byte, 1024)
n := runtime.Stack(buf, false)
fmt.Printf(" 调用栈:\\n%s\\n", buf[:n])
}
}()
level1()
}()
// panic 的中途拦截
fmt.Printf(" panic 的中途拦截:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 最外层捕获: %v\\n", r)
}
}()
interceptedLevel1()
}()
// panic 的重新抛出
fmt.Printf(" panic 的重新抛出:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 最终处理 panic: %v\\n", r)
}
}()
rethrowLevel1()
}()
fmt.Println()
}
// demonstratePracticalUsage 演示 panic/recover 的实际应用
func demonstratePracticalUsage() {
fmt.Println("5. Panic/Recover 的实际应用:")
// Web 服务器中的 panic 恢复
fmt.Printf(" Web 服务器中的 panic 恢复:\\n")
requests := []string{
"/api/users",
"/api/panic",
"/api/orders",
"/api/error",
}
for _, path := range requests {
handleHTTPRequest(path)
}
// 工作池中的 panic 处理
fmt.Printf(" 工作池中的 panic 处理:\\n")
jobs := []Job{
{ID: 1, Data: "正常任务"},
{ID: 2, Data: "panic任务"},
{ID: 3, Data: "正常任务"},
{ID: 4, Data: "error任务"},
}
for _, job := range jobs {
processJobSafely(job)
}
// 数据解析中的 panic 处理
fmt.Printf(" 数据解析中的 panic 处理:\\n")
data := []string{
"123",
"abc",
"456",
"",
"789",
}
for _, item := range data {
result := parseIntSafely(item)
fmt.Printf(" 解析 '%s': %d\\n", item, result)
}
// 递归函数中的 panic 处理
fmt.Printf(" 递归函数中的 panic 处理:\\n")
testValues := []int{5, -1, 10, 0}
for _, n := range testValues {
result := safeFactorial(n)
fmt.Printf(" %d! = %d\\n", n, result)
}
fmt.Println()
}
// demonstrateBestPractices 演示最佳实践
func demonstrateBestPractices() {
fmt.Println("6. 最佳实践:")
// 最佳实践原则
fmt.Printf(" 最佳实践原则:\\n")
fmt.Printf(" 1. 优先使用错误返回值而不是 panic\\n")
fmt.Printf(" 2. 只在真正异常的情况下使用 panic\\n")
fmt.Printf(" 3. 在库代码中避免 panic转换为错误\\n")
fmt.Printf(" 4. 在应用边界使用 recover 保护\\n")
fmt.Printf(" 5. 记录 panic 信息用于调试\\n")
// 库函数的 panic 处理
fmt.Printf(" 库函数的 panic 处理:\\n")
// 不好的做法:库函数直接 panic
fmt.Printf(" 不好的做法示例:\\n")
result, err := callLibraryFunction("invalid")
if err != nil {
fmt.Printf(" 库函数错误: %v\\n", err)
} else {
fmt.Printf(" 库函数结果: %s\\n", result)
}
// 好的做法:将 panic 转换为错误
fmt.Printf(" 好的做法示例:\\n")
result, err = safeLibraryFunction("invalid")
if err != nil {
fmt.Printf(" 安全库函数错误: %v\\n", err)
} else {
fmt.Printf(" 安全库函数结果: %s\\n", result)
}
// panic 信息的记录
fmt.Printf(" panic 信息的记录:\\n")
func() {
defer func() {
if r := recover(); r != nil {
logPanic(r)
}
}()
panic("需要记录的 panic")
}()
// 优雅的服务关闭
fmt.Printf(" 优雅的服务关闭:\\n")
server := &Server{name: "测试服务器"}
server.Start()
// 模拟服务运行中的 panic
func() {
defer server.handlePanic()
panic("服务运行时错误")
}()
server.Stop()
fmt.Println()
}
// demonstrateErrorVsPanic 演示错误处理 vs panic 的选择
func demonstrateErrorVsPanic() {
fmt.Println("7. 错误处理 vs Panic 的选择:")
// 使用错误的场景
fmt.Printf(" 使用错误的场景:\\n")
fmt.Printf(" - 预期可能发生的错误\\n")
fmt.Printf(" - 用户输入错误\\n")
fmt.Printf(" - 网络或I/O错误\\n")
fmt.Printf(" - 业务逻辑错误\\n")
// 使用 panic 的场景
fmt.Printf(" 使用 panic 的场景:\\n")
fmt.Printf(" - 程序逻辑错误\\n")
fmt.Printf(" - 不可恢复的错误\\n")
fmt.Printf(" - 初始化失败\\n")
fmt.Printf(" - 断言失败\\n")
// 对比示例
fmt.Printf(" 对比示例:\\n")
// 错误处理示例
fmt.Printf(" 错误处理示例:\\n")
result, err := divide(10, 0)
if err != nil {
fmt.Printf(" 除法错误: %v\\n", err)
} else {
fmt.Printf(" 除法结果: %.2f\\n", result)
}
// panic 示例
fmt.Printf(" panic 示例:\\n")
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 捕获 panic: %v\\n", r)
}
}()
assertPositive(-5)
}()
// 混合使用示例
fmt.Printf(" 混合使用示例:\\n")
calculator := &Calculator{}
// 正常计算
result, err = calculator.Calculate("10 + 5")
if err != nil {
fmt.Printf(" 计算错误: %v\\n", err)
} else {
fmt.Printf(" 计算结果: %.2f\\n", result)
}
// 无效表达式
result, err = calculator.Calculate("invalid")
if err != nil {
fmt.Printf(" 计算错误: %v\\n", err)
} else {
fmt.Printf(" 计算结果: %.2f\\n", result)
}
// 内部 panic 被转换为错误
result, err = calculator.Calculate("panic")
if err != nil {
fmt.Printf(" 计算错误: %v\\n", err)
} else {
fmt.Printf(" 计算结果: %.2f\\n", result)
}
fmt.Println()
}
// ========== 辅助函数和类型定义 ==========
// safeFunction 安全执行函数
func safeFunction(fn func() int) int {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 函数执行中发生 panic已恢复: %v\\n", r)
}
}()
return fn()
}
// level1 第一层函数
func level1() {
defer func() {
fmt.Printf(" level1 defer 执行\\n")
}()
fmt.Printf(" 进入 level1\\n")
level2()
fmt.Printf(" level1 正常结束\\n")
}
// level2 第二层函数
func level2() {
defer func() {
fmt.Printf(" level2 defer 执行\\n")
}()
fmt.Printf(" 进入 level2\\n")
level3()
fmt.Printf(" level2 正常结束\\n")
}
// level3 第三层函数
func level3() {
defer func() {
fmt.Printf(" level3 defer 执行\\n")
}()
fmt.Printf(" 进入 level3\\n")
panic("level3 中的 panic")
fmt.Printf(" level3 正常结束\\n")
}
// interceptedLevel1 中途拦截的第一层
func interceptedLevel1() {
defer func() {
fmt.Printf(" interceptedLevel1 defer\\n")
}()
fmt.Printf(" 进入 interceptedLevel1\\n")
interceptedLevel2()
fmt.Printf(" interceptedLevel1 正常结束\\n")
}
// interceptedLevel2 中途拦截的第二层
func interceptedLevel2() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" interceptedLevel2 拦截 panic: %v\\n", r)
// 不重新抛出panic 在这里被处理
}
fmt.Printf(" interceptedLevel2 defer\\n")
}()
fmt.Printf(" 进入 interceptedLevel2\\n")
interceptedLevel3()
fmt.Printf(" interceptedLevel2 正常结束\\n")
}
// interceptedLevel3 中途拦截的第三层
func interceptedLevel3() {
defer func() {
fmt.Printf(" interceptedLevel3 defer\\n")
}()
fmt.Printf(" 进入 interceptedLevel3\\n")
panic("interceptedLevel3 中的 panic")
fmt.Printf(" interceptedLevel3 正常结束\\n")
}
// rethrowLevel1 重新抛出的第一层
func rethrowLevel1() {
defer func() {
fmt.Printf(" rethrowLevel1 defer\\n")
}()
fmt.Printf(" 进入 rethrowLevel1\\n")
rethrowLevel2()
fmt.Printf(" rethrowLevel1 正常结束\\n")
}
// rethrowLevel2 重新抛出的第二层
func rethrowLevel2() {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" rethrowLevel2 捕获并重新抛出 panic: %v\\n", r)
panic(fmt.Sprintf("重新抛出: %v", r)) // 重新抛出
}
fmt.Printf(" rethrowLevel2 defer\\n")
}()
fmt.Printf(" 进入 rethrowLevel2\\n")
rethrowLevel3()
fmt.Printf(" rethrowLevel2 正常结束\\n")
}
// rethrowLevel3 重新抛出的第三层
func rethrowLevel3() {
defer func() {
fmt.Printf(" rethrowLevel3 defer\\n")
}()
fmt.Printf(" 进入 rethrowLevel3\\n")
panic("rethrowLevel3 中的 panic")
fmt.Printf(" rethrowLevel3 正常结束\\n")
}
// handleHTTPRequest 处理HTTP请求
func handleHTTPRequest(path string) {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" HTTP请求 %s 发生 panic已恢复: %v\\n", path, r)
// 在实际应用中,这里会返回 500 错误
}
}()
fmt.Printf(" 处理请求: %s\\n", path)
switch path {
case "/api/panic":
panic("模拟处理器 panic")
case "/api/error":
// 这里应该返回错误而不是 panic
fmt.Printf(" 请求处理出错\\n")
default:
fmt.Printf(" 请求处理成功\\n")
}
}
// Job 任务结构
type Job struct {
ID int
Data string
}
// processJobSafely 安全处理任务
func processJobSafely(job Job) {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 任务 %d 处理时发生 panic已恢复: %v\\n", job.ID, r)
}
}()
fmt.Printf(" 处理任务 %d: %s\\n", job.ID, job.Data)
switch job.Data {
case "panic任务":
panic("任务处理中的 panic")
case "error任务":
fmt.Printf(" 任务处理出错\\n")
default:
fmt.Printf(" 任务处理成功\\n")
}
}
// parseIntSafely 安全解析整数
func parseIntSafely(s string) int {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 解析 '%s' 时发生 panic: %v\\n", s, r)
}
}()
if s == "" {
panic("空字符串无法解析")
}
result, err := strconv.Atoi(s)
if err != nil {
return 0 // 返回默认值
}
return result
}
// safeFactorial 安全计算阶乘
func safeFactorial(n int) int {
defer func() {
if r := recover(); r != nil {
fmt.Printf(" 计算 %d! 时发生 panic: %v\\n", n, r)
}
}()
return factorial(n)
}
// factorial 计算阶乘(可能 panic
func factorial(n int) int {
if n < 0 {
panic("负数没有阶乘")
}
if n == 0 || n == 1 {
return 1
}
return n * factorial(n-1)
}
// callLibraryFunction 库函数(不好的做法)
func callLibraryFunction(input string) (string, error) {
// 这个函数直接 panic不是好的做法
if input == "invalid" {
panic("库函数中的 panic")
}
return "处理结果: " + input, nil
}
// safeLibraryFunction 安全的库函数
func safeLibraryFunction(input string) (result string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("库函数内部错误: %v", r)
}
}()
// 调用可能 panic 的函数
return callLibraryFunction(input)
}
// logPanic 记录 panic 信息
func logPanic(r interface{}) {
fmt.Printf(" [PANIC LOG] 时间: %s\\n", time.Now().Format("2006-01-02 15:04:05"))
fmt.Printf(" [PANIC LOG] 错误: %v\\n", r)
fmt.Printf(" [PANIC LOG] 类型: %T\\n", r)
// 获取调用栈
buf := make([]byte, 1024)
n := runtime.Stack(buf, false)
fmt.Printf(" [PANIC LOG] 调用栈:\\n%s\\n", buf[:n])
}
// Server 服务器结构
type Server struct {
name string
}
func (s *Server) Start() {
fmt.Printf(" 服务器 %s 启动\\n", s.name)
}
func (s *Server) Stop() {
fmt.Printf(" 服务器 %s 停止\\n", s.name)
}
func (s *Server) handlePanic() {
if r := recover(); r != nil {
fmt.Printf(" 服务器 %s 捕获 panic: %v\\n", s.name, r)
fmt.Printf(" 服务器继续运行\\n")
}
}
// divide 除法运算
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为零")
}
return a / b, nil
}
// assertPositive 断言为正数
func assertPositive(n int) {
if n <= 0 {
panic(fmt.Sprintf("断言失败: %d 不是正数", n))
}
fmt.Printf(" 断言通过: %d 是正数\\n", n)
}
// Calculator 计算器
type Calculator struct{}
func (c *Calculator) Calculate(expression string) (result float64, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("计算表达式 '%s' 时发生内部错误: %v", expression, r)
}
}()
switch expression {
case "10 + 5":
return 15, nil
case "invalid":
return 0, fmt.Errorf("无效的表达式: %s", expression)
case "panic":
panic("计算器内部 panic")
default:
return 0, fmt.Errorf("不支持的表达式: %s", expression)
}
}
/*
运行这个程序:
go run 03-panic-recover.go
学习要点:
1. panic 是运行时错误,会导致程序崩溃
2. recover 只能在 defer 函数中使用来捕获 panic
3. defer 语句按照 LIFO 顺序执行
4. panic 会沿着调用栈向上传播
5. panic/recover 应该谨慎使用,优先使用错误处理
Panic 的特性:
1. 停止当前函数的正常执行
2. 执行当前函数的所有 defer 语句
3. 向上传播到调用函数
4. 如果没有被 recover程序会崩溃
5. 可以携带任意类型的值
Recover 的特性:
1. 只能在 defer 函数中直接调用
2. 返回 panic 的值,如果没有 panic 返回 nil
3. 只能捕获当前 goroutine 的 panic
4. 捕获后程序可以继续执行
5. 可以根据 panic 值进行不同处理
常见的 Panic 场景:
1. 数组或切片越界访问
2. 空指针解引用
3. 类型断言失败(不安全断言)
4. 向已关闭的 channel 发送数据
5. 除零操作(整数除法)
使用场景:
1. Panic 适用场景:
- 程序逻辑错误
- 不可恢复的错误
- 初始化失败
- 断言失败
2. Error 适用场景:
- 预期可能发生的错误
- 用户输入错误
- 网络或I/O错误
- 业务逻辑错误
最佳实践:
1. 优先使用错误返回值而不是 panic
2. 只在真正异常的情况下使用 panic
3. 在库代码中避免 panic转换为错误
4. 在应用边界使用 recover 保护
5. 记录 panic 信息用于调试
6. 使用 defer 进行资源清理
注意事项:
1. recover 只能在 defer 中直接调用
2. 不要忽略 recover 的返回值
3. panic 会影响程序性能
4. 过度使用 panic/recover 会使代码难以理解
5. 在 goroutine 中的 panic 需要单独处理
实际应用:
1. Web 服务器的 panic 恢复中间件
2. 工作池中的任务 panic 处理
3. 数据解析中的异常处理
4. 递归函数的栈溢出保护
5. 库函数的 panic 转错误
性能考虑:
1. panic/recover 有一定的性能开销
2. 不应该用于正常的控制流
3. 频繁的 panic/recover 会影响性能
4. 错误处理通常比 panic/recover 更高效
5. 在性能敏感的代码中谨慎使用
*/