Defer

Other topics

Remarks:

Defer works by injecting a new stack frame (the called function after the defer keyword) into the call stack below the currently executing function. This means that defer is guaranteed to run as long as the stack will be unwound (if your program crashes or gets a SIGKILL, defer will not execute).

Defer Basics

A defer statement in Go is simply a function call marked to be executed at a later time. Defer statement is an ordinary function call prefixed by the keyword defer.

defer someFunction()

A deferred function is executed once the function that contains the defer statement returns. Actual call to the deferred function occurs when the enclosing function:

  • executes a return statement
  • falls off the end
  • panics

Example:

func main() {
    fmt.Println("First main statement")
    defer logExit("main") // position of defer statement here does not matter
    fmt.Println("Last main statement")
}

func logExit(name string) {
    fmt.Printf("Function %s returned\n", name)
}

Output:

First main statement
Last main statement
Function main returned

If a function has multiple deferred statements, they form a stack. The last defer is the first one to execute after the enclosing function returns, followed by subsequent calls to preceding defers in order (below example returns by causing a panic):

func main() {
    defer logNum(1)
    fmt.Println("First main statement")
    defer logNum(2)
    defer logNum(3)
    panic("panic occurred")
    fmt.Println("Last main statement") // not printed
    defer logNum(3) // not deferred since execution flow never reaches this line
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

Output:

First main statement
Num 3
Num 2
Num 1
panic: panic occurred

goroutine 1 [running]:
....

Note that deferred functions have their arguments evaluated at the time defer executes:

func main() {
    i := 1
    defer logNum(i) // deferred function call: logNum(1)
    fmt.Println("First main statement")
    i++
    defer logNum(i) // deferred function call: logNum(2)
    defer logNum(i*i) // deferred function call: logNum(4)
    return // explicit return
}

func logNum(i int) {
    fmt.Printf("Num %d\n", i)
}

Output:

First main statement
Num 4
Num 2
Num 1

If a function has named return values, a deferred anonymous function within that function can access and update the returned value even after the function has returned:

func main() {
    fmt.Println(plusOne(1)) // 2
    return
}

func plusOne(i int) (result int) { // overkill! only for demonstration
    defer func() {result += 1}() // anonymous function must be called by adding ()

    // i is returned as result, which is updated by deferred function above
    // after execution of below return
    return i
}

Finally, a defer statement is generally used operations that often occur together. For example:

  • open and close a file
  • connect and disconnect
  • lock and unlock a mutex
  • mark a waitgroup as done (defer wg.Done())

This use ensures proper release of system resources irrespective of the flow of execution.

resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed

Deferred Function Calls

Deferred function calls serve a similar purpose to things like finally blocks in languages like Java: they ensure that some function will be executed when the outer function returns, regardless of if an error occurred or which return statement was hit in cases with multiple returns. This is useful for cleaning up resources that must be closed like network connections or file pointers. The defer keyword indicates a deferred function call, similarly to the go keyword initiating a new goroutine. Like a go call, function arguments are evaluated immediately, but unlike a go call, deferred functions are not executed concurrently.

func MyFunc() {
    conn := GetConnection()    // Some kind of connection that must be closed.
    defer conn.Close()        // Will be executed when MyFunc returns, regardless of how.
    // Do some things...
    if someCondition {
        return                // conn.Close() will be called
    }
    // Do more things
}// Implicit return - conn.Close() will still be called

Note the use of conn.Close() instead of conn.Close - you're not just passing in a function, you're deferring a full function call, including its arguments. Multiple function calls can be deferred in the same outer function, and each will be executed once in reverse order. You can also defer closures - just don't forget the parens!

defer func(){
    // Do some cleanup
}()

Syntax:

  • defer someFunc(args)
  • defer func(){ //code goes here }()

Contributors

Topic Id: 2795

Example Ids: 9429,10474

This site is not affiliated with any of the contributors.