Defer keyword
Defer keyword can makeFunction or statementWhen delayed to the end of the function statement block, the function will be executed immediately. Even if the function ends with an error in the middle, even if the function has already reported panic(), or even if the function has returned, the object delayed by defer will be executed.
In fact, the essence of defer is that when the defer keyword is used in a certain function, an independent defer stack frame is created, and the defer statement is pushed into the stack, and the relevant variables it uses are also copied to the stack frame (obviously copied by value). Because the stack is LIFO, press the stack first and then execute it. Because they are independent stack frames, even if the caller functions have returned or reported an error, they can still enter the defer stack frame after them to be executed.
For example:
func main() { a() } func a() { println("in a") defer b() // Push b() into the defer stack println("leaving a") //B() will be executed only after this} func b() { println("in b") println("leaving b") }
The above outputs:
in a leaving a in b leaving b
Even if the function has reported an error or the function has returned, the defer object will be executed at the last moment before the function exits.
func a() TYPE{ ...CODE... defer b() ...CODE... // An error occurred in function execution return args // The function b() will be executed here}
But note that since Go's scope uses a lexical scope, the definition position of defer determines that it delays the variable value that the object can see, rather than delays the value that the object can see when it is called.
For example:
package main var x = 10 func main() { a() } func a() { println("start a:",x) // Output 10 x = 20 defer b(x) // Press the stack and copy 20 to the stack by value x = 30 println("leaving a:",x) // Output 30 // Call defer delayed object b(), output 20} func b(x int) { println("start b:",x) }
Compare the following defers:
package main var x = 10 func main() { a() } func a() int { println("start a:", x) // Output 10 x = 20 defer func() { // Press the stack, but no value is passed, so the internal reference is x println("in defer:", x) // Output 30 }() x = 30 println("leaving a:", x) // Output 30 return x }
The output value of the anonymous function delayed by defer above is 30, shouldn't it see 20? Change it to the following first:
package main var x = 10 func main() { a() } func a() int { println("start a:", x) // Output 10 x = 20 defer func(x int) { println("in defer:", x) // Output 20 }(x) x = 30 println("leaving a:", x) // Output 30 return x }
The object that defers delays sees 20, which is the first typedefer b(x)
It's the same.
The reason is that if the defer is delayed, it directly evaluates parameters and variables at its definition location. The copy value of the copy value, the pointer that meets the pointer that meets the pointer. Therefore, for cases (1) and (3), at the definition position of defer, x=20 is copied to the deferred function parameter, so the internal operation of the function is always a copy of x. The second case is to directly point to the variable x=20 it sees. Then a variable is a global variable. When x=30 is executed, its value will be modified. When the object that defers are executed, the value of x it points to has been modified.
Look at the following example, put defer into a statement block, and declare a variable x of the same name in this statement block:
func a() int { println("start a:", x) // Output 10 x = 20 { x := 40 defer func() { println("in defer:", x) // Output 40 }() } x = 30 println("leaving a:", x) // Output 30 return x }
The above defer is defined in the statement block, and the x it can see is in the statement blockx=40
, its x points to x in the statement block. On the other hand, when the statement block ends,x=40
x will disappear, but since x still points to the value 40 in the defer function, the value 40 is still referenced by the defer function, and it will not be recycled by the GC until the defer is executed. Therefore, when defer's function is executed, it will still output 40.
If there are multiple defers in the statement block, the defer object is executed in the form of LIFO (last in first out), that is, the defer is defined first and then executed.
func main() { println("start...") defer println("1") defer println("2") defer println("3") defer println("4") println("end...") }
Will output:
start... end... 4 3 2 1
What is the use of defer? It is generally used to perform after-care operations, such as cleaning up garbage, releasing resources, and executing the defer object regardless of whether it is reported or not. On the other hand, defer can put these after-care statements and the start statements together, which improves both readability and security. After all, you can write the defer statement directly after writing the start statement, and you will never forget to close, after-care operations.
For example, opening a file and closing the file is written together:
open() defer () ... Operation files ...
Here are some common scenarios for defer:
- Open and close the file
- Lock, release lock
- Establish and release connection
- Output the ending information as the end
- Clean up garbage (such as temporary files)
panic() and recover()
panic() is used to generate error messages and terminateCurrent goroutine, generally regarded as exiting the function where panic() is located and exiting the function where panic() is called. For example, if F() is called in G() and panic() is called in F(), then F() exits and G() also exits.
Note that the object deferred by the defer keyword is the last call of the function, and even if panic appears, the object deferred by the defer will be called.
For example, in the following code, output one in main()start main
After calling a(), it will outputstart a
, then panic, panic() will outputpanic: panic in a
, and then report an error and terminate the program.
func main() { println("start main") a() println("end main") } func a() { println("start a") panic("panic in a") println("end a") }
The execution results are as follows:
start main start a panic: panic in a goroutine 1 [running]: () E:/learning/:14 +0x63 () E:/learning/:8 +0x4c exit status 2
Pay attention to the aboveend a
andend main
None of them were output.
You can use recover() to capture panic() and resume execution. recover() is used to catch panic() error and return this error message. But note that even if recover() captures panic(), calling the function containing the panic() function (i.e. the G() function above) will exit, so if recover() is defined in G(), the code after calling the F() function in G() will not be executed (see the general format below).
The following are the common formats of panic() and recover():
func main() { G() // The following code will be executed ...CODE IN MAIN... } func G(){ defer func (){ if str := recover(); str != nil { (str) } }() ...CODE IN G()... // The call to F() must be after the defer keyword F() // The following code in this function will not be executed ...CODE IN G()... } func F() { ...CODE1... panic("error found") // The following code will not be executed ...CODE IN F()... }
You can use recover() to capture panic() and resume execution. But the following code is wrong:
func main() { println("start main") a() println("end main") } func a() { println("start a") panic("panic in a") // It is wrong to put it directly after panic panic_str := recover() println(panic_str) println("end a") }
The reason for the error is that panic() directly exits the functions a() and main() as soon as it appears. To recover() really capture panic(), you need to place recover() in the defer object, and the definition of defer must be before panic() occurs.
For example, the following is an example of a common format:
package main import "fmt" func main() { println("start main") b() println("end main") } func a() { println("start a") panic("panic in a") println("end a") } func b() { println("start b") defer func() { if str := recover(); str != nil { (str) } }() a() println("end b") }
The following is the output result:
start main start b start a panic in a end main
Pay attention to the aboveend b
、end a
None of them were output, butend main
Output.
panic() is a built-in function (in the package builtin),log
There is also a Panic() function in the package. After calling Print() to output information, panic() is called.go doc log Panic
You can tell at a glance:
$ go doc log Panic func Panic(v ...interface{}) Panic is equivalent to Print() followed by a call to panic().
More AboutDefer, panic and recovery of the Go basic tutorial seriesPlease check the related links below