This is a creation in Article, where the information may have evolved or changed.
First, preface
In real-world projects, there are many best practices for exceptions, and there are different suggestions for developing different types of programs in different languages. Google C + + Style mentions that Google's internal C + + code does not use exceptions, and the community has many discussions about exceptions.
As a relatively new language, the go language does not use the traditional Try...catch-like exception handling mechanism, but instead provides panic and recover functions to handle so-called runtime exceptions, which are Google The alleged error handling mechanism. With defer statements and error interfaces, developers have the flexibility to handle runtime errors and exceptions. Google certainly does not want developers in the code to use panic, we know in the risk control, there is known unknown and unknown unknown, in Go programming, for the former, we can use the pre-developed code branch to deal with, the latter is more tricky:
1. If the code in the project, the standard library used, and the third-party library catch the exception inside the runtime and return it to the caller via the appropriate error object, then we can do as little as possible without panic the function.
2, if the above situation can not be guaranteed, that in order to ensure that the program at runtime will not cause the unknown unknown caused the crash, the use of the panic function may have to be added wherever needed.
We should realize thatpanic is what we and the computer do not want to see, should be in the design and development to fully consider the use of scenarios may occur, to deal with the known unknown, in determining where the need to use the panic mechanism.
Second,defer
The defer keyword is used to mark the last GO statement, which is typically used in resource releases, closing connections, and so on, before the function is closed.
Multiple defer are defined with the execution of a stack-like operation: advanced-out , first-defined final execution.
Please look at the following code, and then determine the respective output:
// 示例代码一:func funcA() int { x := 5 defer func() { x += 1 }() return x}// 示例代码二:func funcB() (x int) { defer func() { x += 1 }() return 5}// 示例代码三:func funcC() (y int) { x := 5 defer func() { x += 1 }() return x} // 示例代码四:func funcD() (x int) { defer func(x int) { x += 1 }(x) return 5}
Parsing these pieces of code, the main need to understand the following knowledge:
1,The return statement processing process
The return XXX statement is not an atomic instruction, and it is executed with the statement decomposed into the return variable =xxx return, and the last execution of the return
2.defer statement Execution time
As mentioned above, the defer statement is called when the function is closed, and is actually called when the return statement is executed, note that return is not return XXX
3 . How to transfer function parameters
In the go language, the normal function parameter is passed in the value pass, that is, the new memory copy variable value, excluding slice and map, these two types are reference passing
4 . How to transfer variable assignment
The assignment of the Go language variable is similar to the function parameter and is also a value copy, excluding slice and map, which are memory references
In accordance with the above principles, parse the code:
// 解析代码一:返回temp的值,在将x赋值给temp后,temp未发生改变,最终返回值为5func funcA() int { x := 5 temp=x #temp变量表示未显示声明的return变量 func() { x += 1 }() return}// 解析代码二:返回x的值,先对其复制5,接着函数中改变为6,最终返回值为6func funcB() (x int) { x = 5 func() { x += 1 }() return}// 解析代码三:返回y的值,在将x赋值给y后,y未发生改变,最终返回值为5func funcC() (y int) { x := 5 y = x #这里是值拷贝 func() { x += 1 }() return}// 解析代码四:返回x的值,传递x到匿名函数中执行时,传递的是x的拷贝,不影响外部x的值,最终返回值为5func funcD() (x int) { x := 5 func(x int) { #这里是值拷贝 x += 1 }(x) return}
Third,Error
The go language makes it very convenient for callers to return detailed error information at run time by supporting multiple return values. We can generate error messages in the encoding by implementing the wrong interface type, which is defined as follows:
type error interface { Error() string}
Let's take a look at the following example:
package mainimport ("fmt")// 定义一个 DivideError 结构type DivideError struct {dividee intdivider int}// 实现 `error` 接口func (de *DivideError) Error() string {strFormat := `Cannot proceed, the divider is zero.dividee: %ddivider: 0`return fmt.Sprintf(strFormat, de.dividee)}// 定义 `int` 类型除法运算的函数func Divide(varDividee int, varDivider int) (result int, errorMsg string) {if varDivider == 0 {dData := DivideError{dividee: varDividee,divider: varDivider,}errorMsg = dData.Error()return} else {return varDividee / varDivider, ""}}func main() {// 正常情况if result, errorMsg := Divide(100, 10); errorMsg == "" {fmt.Println("100/10 = ", result)}// 当被除数为零的时候会返回错误信息if _, errorMsg := Divide(100, 0); errorMsg != "" {fmt.Println("errorMsg is: ", errorMsg)}}
After running, you can see the following output:
100/10 = 10errorMsg is: Cannot proceed, the divider is zero.dividee: 100divider: 0
Iv.Panic and recover
Defined as follows:
func panic(interface{})func recover() interface{}
Panic and recover are two built-in functions for handling Run-time panics and custom errors in programs.
When executing a function f, if you explicitly call the panic function or a run-time panics occurs, F will end up running, and all defer functions in F will be executed according to the FILO rule. After that, the defer function in the caller of the F function is executed again, so that it continues to the outermost code. At this point, the program has been interrupted and the error is thrown in a layer, including the parameters of the panic function. The currently interrupted Goroutine is referred to as being in the panicking state. Because the parameters of the panic function are empty interface types, you can accept objects of any type:
panic(42)panic(42)panic("unreachable")panic(Error("cannot parse"))
recover function to get the parameter information of the panic function. Can only be invoked directly in a function called by the deferred call defer statement, if the panic function is also called in the defer statement, Only the parameters of the last called panic function are obtained by the recover function. If Goroutine does not have panic , the call to the Recover function returns nil .
Package Mainimport ("FMT")//The simplest example of Func simplepanicrecover () {defer func () {if err: = Recover (); Err! = nil {fmt. Println ("Panic info is:", Err)}} () Panic ("Simplepanicrecover function panic-ed!")} When the panic function is also called in the defer, the arguments to the last called panic function are obtained by the recover function in the subsequent//function to define multiple defer functions and execute Func FILO according to the Multipanicrecove rule. R () {defer func () {if err: = Recover (); Err! = nil {fmt. Println ("Panic info is:", Err)}} () defer func () {Panic ("Multipanicrecover defer inner Panic")} () defer func () {if err: = RE Cover (); Err! = Nil {fmt. Println ("Panic info is:", Err)}} () Panic ("Multipanicrecover function panic-ed!")} The Recover function can only get panic parameter func if it is called directly in the defer function recoverplacetest () {///The Recover function in the following line of code returns nil, but it does not affect the program running defer reco Ver ()//Recover function returns Nildefer FMT. Println ("Recover () is:", recover ()) defer func () {func () {///) Defer function returns recover err because it is not called directly recover function in the nilif call function: = R Ecover (); Err! = Nil {fmt. Println ("Panic info is:", Err)}} ()} () defer func () {if err: = Recover (); Err! = Nil {fmt. PrintlN ("Panic info is:", Err)}} () Panic ("Recoverplacetest function panic-ed!")} If the function is not panic, calling the recover function does not get any information, nor does it affect the current process. Func Nopanicbuthasrecover () {if err: = Recover (); Err! = nil {fmt. Println ("Nopanicbuthasrecover Panic info is:", err)} else {fmt. Println ("Nopanicbuthasrecover Panic info is:", err)}}//defines a function called Recover function func Callrecover () {if err: = Recover (); Err! = Nil {fmt. Println ("Panic info is:", err)}}//defines a function in which defer another function called Recover function func Recoverinoutterfunc () {defer callrecover () pan IC ("Recoverinoutterfunc function panic-ed!")} Func Main () {simplepanicrecover () Multipanicrecover () recoverplacetest () Nopanicbuthasrecover () Recoverinoutterfunc ( )}
After running, you can see the following output:
Panic info is: SimplePanicRecover function panic-ed!Panic info is: MultiPanicRecover function panic-ed!Panic info is: MultiPanicRecover defer inner panicPanic info is: RecoverPlaceTest function panic-ed!recover() is: <nil>NoPanicButHasRecover Panic info is: <nil>Panic info is: RecoverInOutterFunc function panic-ed!