1. 拦截 panic

// $GOROOT/src/bytes/buffer.go
func makeSlice(n int) []byte {
  // If the make fails, give a known error.
  defer func() {
      if recover() != nil {
          panic(ErrTooLarge) // 触发一个新panic
      }
  }()
  return make([]byte, n)
}

defer function 中是用 recover 是 Golang 中唯一从 panic 中恢复的手段,能够拦截运行时中的 panic,但是对运行时之外触发的崩溃无法捕获,比如调用 C 语言的库,C 语言中发生的崩溃。

2. 修改函数的具名返回值

下面是一个标准库中的例子:

// $GOROOT/src/fmt/scan.go
func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
    defer func() {
        if e := recover(); e != nil {
            if se, ok := e.(scanError); ok {
                err = se.err
            } else {
                panic(e)
            }
        }
    }()
    ...
}

3. 输出调试信息

下面这个例子通过 defer 给函数加上了调用和退出的调试日志。

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}
 
func un(s string) {
    fmt.Println("leaving:", s)
}
 
func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}
 
func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}
 
func main() {
    b()
}

4. 还原变量旧值

下面同样是一个来自标准库的例子:

// $GOROOT/src/syscall/fs_nacl.go
func init() {
    oldFsinit := fsinit
    defer func() { fsinit = oldFsinit }()
    fsinit = func() {}
    Mkdir("/dev", 0555)
    Mkdir("/tmp", 0777)
    mkdev("/dev/null", 0666, openNull)
    mkdev("/dev/random", 0444, openRandom)
    mkdev("/dev/urandom", 0444, openRandom)
    mkdev("/dev/zero", 0666, openZero)
    chdirEnv()
}