[Awesome Go] Use panic and recover to handle errors and exceptions - os.Exit - log.Fatal - panic - defer - recover - Programming Language - Golang (Go) - Exception Error Handling

panic(v interface{}) and recover() interface{}

In the Go language, an error is considered an expected result, but an exception is an unexpected result. An exception may indicate that there is a Bug in the program or other uncontrollable problems have occurred.

If an error is encountered during internal recursive code, an exception will be thrown to quickly jump out of the deeply nested function call, and then the outermost interface will capture panic through recover, and then return the corresponding error message.

panic(v interface{}) and recover() interface{}

Go language recommends using the recover built-in function to turn internal exceptions into error handling, so that users can really care about business-related error handling.

Panic is similar to throwing an exception. In other words, a panic is an exception in Go. A panic is caused either by a runtime error, or an explicit call to the built-in panic function.

A panic is caused either by a runtime error or an explicit call to the built-in panic function.

Let’s see function panic(v interface{}) definition.

1
2
3
4
5
6
7
8
9
10
11
12
// The panic built-in function stops normal execution of the current
// goroutine. When a function F calls panic, normal execution of F stops
// immediately. Any functions whose execution was deferred by F are run in
// the usual way, and then F returns to its caller. To the caller G, the
// invocation of F then behaves like a call to panic, terminating G's
// execution and running any deferred functions. This continues until all
// functions in the executing goroutine have stopped, in reverse order. At
// that point, the program is terminated and the error condition is reported,
// including the value of the argument to panic. This termination sequence
// is called panicking and can be by the built-in function
// recover.
func panic(v interface{})

Let’s see function recover() interface{} definition.

1
// The recover built-in function allows a program to manage behavior of a panicking goroutine. Executing a call to recover inside a deferred function (but not any function called by it) stops the panicking sequence by restoring normal execution and retrieves the error value passed to the call of panic. If recover is called outside the deferred function it will not stop a panicking sequence. In this case, or when the goroutine is not panicking, or if the argument supplied to panic was nil, recover returns nil. Thus the return value from recover reports whether the goroutine is panicking.

Key points:


  1. Stops normal execution of the current function F and the current goroutine.

  2. Any functions whose execution was deferred by the current function F and the current goroutine.

  3. Then the current function F returns to its caller G.

  4. the caller G behaves like a call to panic, terminating G’s execution and running any deferred functions, likes 1, 2, 3.

  5. This continues until all functions in the executing goroutine have stopped, in reverse order.

  6. The program is terminated and the error condition is reported, including the value of the argument to panic.

  7. This termination sequence can be controlled by the built-in function recover.


Example: main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Use `panic` to terminate program and the error condition is reported,
// including the value of the argument to panic.
// Then use `recover` to controll the termination sequence.
package main

import (
"log"
)

func foo() {
defer func() {
log.Println("1. defer foo")

// controll the termination sequence
if r := recover(); r != nil {
log.Println("2. Recovered in foo", r)
}

// Retry invoke recover().
log.Println("2.1 Retry invoke recover() will return nil!")
}()

// Invoke recover() outside defered function.
log.Println("0. recover() will return nil outside defered function!")

panic("foo")
}

func bar() {
defer func() { log.Println("3. defer bar") }()
panic("bar")
}

func foobar() {
defer func() { log.Println("4. defer foobar") }()

foo()
bar()
}

func main() {

// `defer`s will be run when using `panic`, so
// this `log.Println` will be called.
defer log.Println("5. main!")

foobar()

// Panic with with a given message.
panic("Panic")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ go run main.go
2009/11/10 23:00:00 0. recover() will return nil!
2009/11/10 23:00:00 1. defer foo
2009/11/10 23:00:00 2. Recovered in foo foo
2009/11/10 23:00:00 2.1 recover() will return nil!
2009/11/10 23:00:00 3. defer bar
2009/11/10 23:00:00 4. defer foobar
2009/11/10 23:00:00 5. main!
panic: bar

goroutine 1 [running]:
main.bar()
/tmp/sandbox620540596/prog.go:31 +0x5b
main.foobar()
/tmp/sandbox620540596/prog.go:38 +0x4a
main.main()
/tmp/sandbox620540596/prog.go:47 +0x85

Key points about recover() interface{}.


  1. the built-in function recover only execute within the deferred function, otherwise recover will return nil

  2. Once the panic is captured by the recover, it will not continue to spread.


References

[1] Defer, Panic, and Recover - The Go Blog - https://blog.golang.org/defer-panic-and-recover

[2] The Go Programming Language - https://golang.org/