Welcome to tutorial no. 29 in Golang tutorial series.
What is Defer?
Defer statement is used to execute a function call just before the surrounding function where the defer statement is present returns. The definition might seem complex but it's pretty simple to understand by means of an example.
Example
package main
import (
"fmt"
)
func finished() {
fmt.Println("Finished finding largest")
}
func largest(nums []int) {
defer finished()
fmt.Println("Started finding largest")
max := nums[0]
for _, v := range nums {
if v > max {
max = v
}
}
fmt.Println("Largest number in", nums, "is", max)
}
func main() {
nums := []int{78, 109, 2, 563, 300}
largest(nums)
}
largestdefer finished()finished()largest
Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest
finishedFinished finding largest
Deferred methods
Defer is not restricted only to functions. It is perfectly legal to defer a method call too. Let's write a small program to test this.
package main
import (
"fmt"
)
type person struct {
firstName string
lastName string
}
func (p person) fullName() {
fmt.Printf("%s %s",p.firstName,p.lastName)
}
func main() {
p := person {
firstName: "John",
lastName: "Smith",
}
defer p.fullName()
fmt.Printf("Welcome ")
}
In the above program we have deferred a method call in line no. 22. The rest of the program is self explanatory. This program outputs,
Welcome John Smith
Arguments evaluation
defer
Let's understand this by means of an example.
package main
import (
"fmt"
)
func printA(a int) {
fmt.Println("value of a in deferred function", a)
}
func main() {
a := 5
defer printA(a)
a = 10
fmt.Println("value of a before deferred function call", a)
}
a5aprintAaa
value of a before deferred function call 10
value of a in deferred function 5
a10printA(a)5
Stack of defers
When a function has multiple defer calls, they are pushed on to a stack and executed in Last In First Out (LIFO) order.
We will write a small program which prints a string in reverse using a stack of defers.
package main
import (
"fmt"
)
func main() {
name := "Naveen"
fmt.Printf("Original String: %s\n", string(name))
fmt.Printf("Reversed String: ")
for _, v := range name {
defer fmt.Printf("%c", v)
}
}
for rangedefer fmt.Printf("%c", v)
defer fmt.Printf("%c", 'n')
This program will output,
Original String: Naveen
Reversed String: neevaN
Practical use of defer
The code samples we saw so far don't show the practical use of defer. In this section we will look into some practical uses of defer.
Defer is used in places where a function call should be executed irrespective of the code flow. Let's understand this with the example of a program which makes use of WaitGroup. We will first write the program without using defer and then we will modify it to use defer and understand how useful defer is.
package main
import (
"fmt"
"sync"
)
type rect struct {
length int
width int
}
func (r rect) area(wg *sync.WaitGroup) {
if r.length < 0 {
fmt.Printf("rect %v's length should be greater than zero\n", r)
wg.Done()
return
}
if r.width < 0 {
fmt.Printf("rect %v's width should be greater than zero\n", r)
wg.Done()
return
}
area := r.length * r.width
fmt.Printf("rect %v's area %d\n", r, area)
wg.Done()
}
func main() {
var wg sync.WaitGroup
r1 := rect{-67, 89}
r2 := rect{5, -67}
r3 := rect{8, 9}
rects := []rect{r1, r2, r3}
for _, v := range rects {
wg.Add(1)
go v.area(&wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
rectarearect
mainr1r2r3rectrectsfor rangeareawgwg.Done()defer
Let's rewrite the above program using defer.
wg.Done()defer wg.Done()
package main
import (
"fmt"
"sync"
)
type rect struct {
length int
width int
}
func (r rect) area(wg *sync.WaitGroup) {
defer wg.Done()
if r.length < 0 {
fmt.Printf("rect %v's length should be greater than zero\n", r)
return
}
if r.width < 0 {
fmt.Printf("rect %v's width should be greater than zero\n", r)
return
}
area := r.length * r.width
fmt.Printf("rect %v's area %d\n", r, area)
}
func main() {
var wg sync.WaitGroup
r1 := rect{-67, 89}
r2 := rect{5, -67}
r3 := rect{8, 9}
rects := []rect{r1, r2, r3}
for _, v := range rects {
wg.Add(1)
go v.area(&wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}
This program outputs,
rect {8 9}'s area 72
rect {-67 89}'s length should be greater than zero
rect {5 -67}'s width should be greater than zero
All go routines finished executing
areaifwg.Done()wg.Done()wg.Done()
This brings us to the end of this tutorial. Have a good day.
Next tutorial - Error Handling