package main import ( "errors" "fmt" "os" ) // ========== 1. 自定义错误 + fmt.Errorf 封装 ========== func divide(a, b float64) (float64, error) { if b == 0 { // 使用 fmt.Errorf 包装原始错误,添加上下文 return 0, fmt.Errorf("divide by zero: cannot divide %.2f by %.2f", a, b) } return a / b, nil } // 更复杂的错误链(Go 1.13+ 支持 %w) var ErrNegativeInput = errors.New("input must be non-negative") func sqrt(x float64) (float64, error) { if x < 0 { // 使用 %w 包装错误,支持 errors.Is 和 errors.As return 0, fmt.Errorf("invalid input for sqrt: %w", ErrNegativeInput) } return x * x, nil // 注意:这里故意写成平方,方便测试 } // ========== 2. defer 的典型用途 ========== func readFile(filename string) error { fmt.Println("尝试打开文件:", filename) file, err := os.Open(filename) if err != nil { return fmt.Errorf("无法打开文件 %s: %w", filename, err) } // defer 确保文件在函数退出前关闭(即使 panic) defer func() { fmt.Println("defer: 关闭文件") file.Close() }() // 模拟读取 fmt.Println("文件已打开,正在读取...") return nil } // ========== 3. panic 与 recover ========== func riskyFunction(n int) { defer func() { if r := recover(); r != nil { fmt.Printf("recover 捕获到 panic: %v\n", r) } }() if n < 0 { panic("n 不能为负数!") } fmt.Println("riskyFunction 正常执行,n =", n) } // recover 只在 defer 中有效! func noRecover() { // ❌ 这样写无法捕获 panic recover() panic("不会被捕获") } // ========== 4. 综合示例:安全计算 ========== func safeCompute(a, b float64) { defer fmt.Println("safeCompute 结束\n---") fmt.Printf("计算 %.2f / %.2f\n", a, b) result, err := divide(a, b) if err != nil { fmt.Println("错误:", err) return } fmt.Printf("结果: %.2f\n", result) fmt.Println("尝试对结果开方(实际是平方)") sq, err := sqrt(result) if err != nil { if errors.Is(err, ErrNegativeInput) { fmt.Println("检测到特定错误:输入为负") } fmt.Println("sqrt 错误:", err) return } fmt.Printf("平方结果: %.2f\n", sq) } // ========== 主函数 ========== func main() { fmt.Println("=== 1. 基本 error 处理 ===") safeCompute(10, 2) safeCompute(10, 0) // 触发除零错误 fmt.Println("\n=== 2. defer 文件操作 ===") fileErr := readFile("/nonexistent/file.txt") // 文件不存在,触发错误 if fileErr != nil { fmt.Println("文件操作错误:", fileErr) } fileErr2 := readFile("/etc/hostname") // 存在的文件(Linux 系统通常有) if fileErr2 != nil { fmt.Println("文件操作错误:", fileErr2) } fmt.Println("\n=== 3. panic 与 recover ===") riskyFunction(5) riskyFunction(-1) // 触发 panic,但被 recover 捕获 fmt.Println("\n=== 4. 错误封装与识别 ===") _, err := sqrt(-4) if err != nil { fmt.Println("原始错误信息:", err) // 使用 errors.Is 判断是否包含特定错误 if errors.Is(err, ErrNegativeInput) { fmt.Println("✅ 成功识别自定义错误 ErrNegativeInput") } } // ⚠️ 取消注释下面这行会 crash(recover 不在 defer 中无效) //noRecover() }