Skip to content

函数与错误处理

defer LIFO、panic/recover、多返回值错误惯例。

Updated View as Markdown
For humans

函数与错误处理

Go 的错误处理哲学:错误是值,显式处理。面试主线:defer 机制、panic/recover 边界、错误惯例。

多返回值错误惯例

f, err := os.Open("file.txt")
if err != nil {
    return err          // 错误向上传播
}
defer f.Close()
  • Go 没有异常,函数返回 (结果, error),调用方显式检查
  • 错误是值:可包装(fmt.Errorf + %w)、可比较、可自定义类型
  • 优点:错误路径看得见(不像异常隐式抛出);缺点:代码啰嗦(if err != nil 满天飞)
  • 惯例:错误先于结果返回func f() (int, error));错误信息小写开头、不以标点结尾(golint ST1005,因为错误常被 %w 包装拼接)
if err := doSomething(); err != nil {
    return fmt.Errorf("doSomething: %w", err)   // 包装保留链路
}

defer:延迟执行

func readFile() error {
    f, err := os.Open("x.txt")
    if err != nil {
        return err
    }
    defer f.Close()        // 函数返回前执行, 无论成败
    // 使用 f
    return nil
}

defer 三考点

  1. LIFO 顺序:多个 defer 后进先出(资源释放顺序:先开的后关)
  2. 参数即时求值:defer 的参数在声明时求值(不是执行时)
i := 0
defer fmt.Println(i)   // 打印 0, 不是 1
i++
  1. 执行时机:所在函数返回前执行(return 语句求值后、真正返回前),defer 里可修改返回值(命名返回值时)

panic 与 recover

  • panic:运行时严重错误(数组越界、map 并发写),程序崩溃退出(执行所有 defer 后)
  • recover:在 defer 里捕获 panic,让程序恢复
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recovered:", r)   // 捕获, 函数返回, 程序继续
        }
    }()
    panic("boom")
}

工程边界(必考)

  • panic 用于不可恢复的错误(程序无法继续:初始化失败、内存问题)
  • 可预期错误用 error 返回值(文件不存在、参数错)
  • recover 只用于进程级兜底(HTTP 中间件捕获 handler panic 返回 500,不让服务崩溃)
  • recover 只能在同一 goroutine 的 defer 里生效(其他 goroutine 的 panic 捕获不到)

面试追问

  1. defer 的执行顺序? LIFO:后声明的先执行。用于资源释放(先开的后关)
  2. defer 参数何时求值? 声明时(即时求值)。想取执行时值用闭包
  3. panic 和 error 的分工? 可预期错误用 error,不可恢复用 panic。recover 只在进程级兜底用
  4. recover 的限制? 必须在本 goroutine 的 defer 里。goroutine 里的 panic 捕获不到,会崩整个进程
  5. 错误怎么包装? fmt.Errorf + %w,errors.Is/As 解包判断。保留错误链路
Navigation

Type to search…

↑↓ navigate↵ selectEsc close