Welcome to Golang tutorial series of 29.
What is defer?
defer
Purpose statement is: contains defer
function statement, it will be before the function returns, call another function. This definition may seem very complex, we use an example it is easy to understand.
Examples
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) }
The above procedure is very simple, is to find the maximum value for a given slice. largest
A receiving function of type int slice as a parameter, and a maximum value of the print slice. largest
The first line of the function statement defer finished()
. This means that finished()
before the function will return, calls the finished()
function. Run the program, you will see the following output:
Started finding largest
Largest number in [78 109 2 563 300] is 563 Finished finding largest
largest
After the function started, it will print two lines of output above. And in largest
time to be returned, and we call delay function (Deferred Function), print out the Finished finding largest
text.
Delay method
defer
Is not limited to a function call, call the method is legal. We wrote a small program to test it.
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 example above, we delayed a method call on line 22. The rest of the code is straightforward, it is not explained here. The program prints:
Welcome John Smith
The value argument (Arguments Evaluation)
In the Go language, not when calling a delay function arguments before deciding, but when the execution defer
time of the statement, it will delay function arguments are evaluated.
Through an example will be able to understand.
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) }
Line 11, in the above procedures a
an initial value of 5. Line 12 performs the defer
time statement, since a
equal to 5, and therefore the delay function printA
arguments is also equal to 5. In the first 13 rows We then a
modified to 10 values. The next line will print out a
the value. The program prints:
value of a before deferred function call 10
value of a in deferred function 5
From the above output, we can see that in the call defer
after the statement, although we will a
amend 10, but delayed function call printA(a)
, the print is still 5.
defer 栈
When the function is called multiple times within a defer
time, Go will defer
call placed in a stack, and then according to the LIFO (Last In First Out, LIFO) order of execution.
Here we write a small program that uses the defer
stack to a string in reverse print.
package main
import ( "fmt" ) func main() { name := "Naveen" fmt.Printf("Orignal String: %s\n", string(name)) fmt.Printf("Reversed String: ") for _, v := range []rune(name) { defer fmt.Printf("%c", v) } }
Line 11 in the above procedure, the for range
loop iterates a string, and the call in line 12 defer fmt.Printf("%c", v)
. These delays are added to a call stack, performed in LIFO order, therefore, the string is printed in reverse. The program will output:
Orignal String: Naveen
Reversed String: neevaN
Defer the practical application of
So far, the code sample we have seen, did not reflect the defer
actual use. In this section we will look at defer
practical application.
When a function to be independent of the call at the current code stream (Code Flow) environment, it may be used defer
. We used the a WaitGroup
to understand the meaning of this sentence code examples. We will first write an unused defer
program, then we will use defer
to modify, to see defer
the benefits.
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") }
In the above procedure, we created the line 8 in rect
the structure, and a line 13 to create rect
a method area
to calculate the area of the rectangle. area
Check that the length and width of the rectangle is less than zero. If the length and width of the rectangle is less than zero, it will print out a corresponding message, and if greater than zero, it will print out a rectangular area.
main
Function creates three rect
types of r1
variables: , r2
and r3
. On line 34, we add these three variables to rects
slice inside. The sections were then used to for range
cycle through, to area
a method as a concurrent Go coroutine call (line 37). We used WaitGroup wg
to ensure that the main
function after the other coroutine executed, execution will end. WaitGroup
Passed as a parameter to area
the method, on line 16, line 21, line 26 and notification main
function, and now it represents a coroutine tasks are complete. If you look closely, you will find wg.Done()
only area
will be called when the function returns. wg.Done()
It should be in area
before the call to be returned, and regardless of the path (Path) the code stream, so we can call only once defer
, to effectively replace wg.Done()
multiple calls .
We used to defer
rewrite the above code.
In the following code, we removed the original program 3 wg.Done
calls, but with a separate defer wg.Done()
replace it (line 14). This makes our code more concise and easy to understand.
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") }
The program will output:
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
In the above procedure, using defer
another advantage. Suppose we use if
conditional statements, and gave area
way to add a return path (Return Path). If you do not use defer
to call wg.Done()
, we have a very careful to ensure that in the return path of this newly added in the call wg.Done()
. Now that we call the delay wg.Done()
, there is no need to add to this new return path wg.Done()
of the.
This tutorial is over. wish you happiness.