TGPL: Deferred Function Calls

73 阅读1分钟

A defer statement is often used with paired operations like open and close, connect and disconnect, or lock and unlock to ensure that resources are released in all cases, no matter how complex the control flow. The right place for a defer statement that releases a resource is immediately after the resource has been successfully acquired.


func ReadFile(filename string) ([]byte, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    return ReadAll(f)
}

or to unlock a mutex

    var mu sync.Mutex
    var m = make(map[string]int)
    func lookup(key string) {
        mu.Lock()
        defer mu.Unlock()
        return m[key]
    }

The defer statement can also be used to pair "on entry" and "on exit" actions when debugging a complex function. The bigSlowOperation function belows calls trace immediately, which does the "on entry" action then returns a function value that, when called, does the corresponding "on exit" action. By deferring a call to the returned function in this way, we can instrument the entry point and all exit points of a function in a single statement and even pass values, like the start time between the two actions. But don't forget the final parentheses in the defer statement, or the "on entry" action will hapen on exit and the on-exit action won't happen at all!

func bigSlowOperation() {
    defer trace("bigSlowOperation")() // don't forget the extra parentheses
    ...
    time.Sleep(10 * time.Second)
}

func trace(msg string) func() {
    start := time.Now()
    log.Printf("enter %s", msg)
    return func() { log.Printf("exit %s (%s)", msg, time.Since(start)) }
}