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).
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:
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 defer
s 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:
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 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
}()