Defer statement in Golang (part I)
Deferred function is declared inside another function and is called when surrounding function exits — either normally or if goroutine panics. This mechanism is used e.g. to do cleanup and close open files or sockets.
Deferred functions are evaluated in LIFO fashion (source code):
func f() {
fmt.Println("f()")
}func g() {
fmt.Println("g()")
}func h() {
fmt.Println("h()")
}func main() {
defer f()
defer g()
defer h()
}
Output:
h()
g()
f()
defer statement can be used anywhere inside the function so it’s not limited only to its beginning (source code):
func f() {
fmt.Println("boo")
}func main() {
fmt.Println("one")
defer f()
fmt.Println("two")
}
Output:
one
two
boo
Anonymous functions
It’s allowed to used both, named and anonymous functions with defer statement (source code):
func main() {
defer func() {
fmt.Println("foo")
}()
}
Output is foo
.
Evaluating deferred function
When deferred function is nil then runtime error is thrown but it’s done when outer function exists not at the moment statement is executed (source code):
func main() {
var f func()
defer f()
fmt.Println("foo")
}
Output:
foo
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0x8d034]goroutine 1 [running]:
main.main()
/tmp/sandbox581176742/prog.go:11 +0xdb
(11th line is the closing bracket of main function)
Function used by defer statement is evaluated at the moment statement is encountered, not when surrounding function exists (source code):
func main() {
var f func()
defer f()
f = func() {
fmt.Println("foo")
}
}
Output:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0x8ce94]
goroutine 1 [running]:
main.main()
/tmp/sandbox806328181/prog.go:13 +0x7b
Evaluating parameters of deferred function
While processing defer statement arguments are evaluated. Those arguments will be used when outer function exists (source code):
func g() int {
fmt.Println("g()")
return 0
}func f(int) {
fmt.Println("f()")
}func main() {
defer f(g())
fmt.Println("Hello world!")
}
Output:
g()
Hello world!
f()
If array is passed and modified before outer function exists then original (at the moment of defer statement) value will be used (source code):
func f(nums [4]int) {
fmt.Printf("%v\n", nums)
}func main() {
nums := [...]int{0,1,2,3}
defer f(nums)
nums[0] += 1
}
Output is [0 1 2 3]
.
If we’ll use type which is a descriptor like slice then modified arguments will be used (source code):
func f(nums []int) {
fmt.Printf("%v\n", nums)
}func main() {
nums := []int{0,1,2,3}
defer f(nums)
nums[0] += 1
}
Output is [1 1 2 3]
. The same is true for e.g. maps (source code):
func f(nums map[string]int) {
fmt.Printf("%v\n", nums)
}func main() {
nums := map[string]int{"one": 1}
defer f(nums)
nums["one"] += 1
}
Output is map[one:2]
.
Changing return value(s) of surrounding function
If outer function has named result value(s) then deferred function has access to it through closure and can modify it (source code):
func f() (v int) {
defer func() {
v *= 2
}()
v = 2
return
}
func main() {
fmt.Println(f())
}
Output is 4
.
Return value(s) of deferred function
Whatever deferred function returns is discarded (source code):
func f() int {
defer func() int {
return 5
}()
return 0
}
func main() {
fmt.Println(f())
}
Output is 0
.
Resources
In 2nd part we’ll discuss handling panics, common uses cases of defer statement and performance of deferred functions. Stay tuned.
👏👏👏 below to help others discover this story. Please follow me here or on Twitter if you want to get updates about new posts or boost work on future stories.