Yesterday we saw how panic
plays with deferred functions, in terms of order of execution.
Today we’ll take things up one level, and throw recover
in there… there’s more to recover
than just execution order. We’ll get to that next.
Handling panics
…
The
recover
function allows a program to manage behavior of a panicking goroutine. Suppose a functionG
defers a functionD
that callsrecover
and a panic occurs in a function on the same goroutine in whichG
is executing. When the running of deferred functions reachesD
, the return value ofD
’s call torecover
will be the value passed to the call ofpanic
. IfD
returns normally, without starting a newpanic
, the panicking sequence stops. In that case, the state of functions called betweenG
and the call topanic
is discarded, and normal execution resumes. Any functions deferred byG
beforeD
are then run andG
’s execution terminates by returning to its caller.
Let’s demonstrate this order of execution by expanding on yesterday’s code example:
func main() {
foo()
}
func foo() {
defer fmt.Println("foo is done")
bar()
}
func bar() {
defer fmt.Println("bar is done")
baz()
}
func baz() {
defer fmt.Println("baz is done")
panic(123)
}
As written, this code outputs:
baz is done
bar is done
foo is done
panic: 123
followed by a stack trace.
Let’s now add a recovery, and see how this affect things.
func main() {
foo()
}
func foo() {
defer fmt.Println("foo is done")
bar()
}
func bar() {
defer fmt.Println("bar is done")
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered", r)
}
}()
defer fmt.Println("an extra defer for good measure")
baz()
}
func baz() {
defer fmt.Println("baz is done")
panic(123)
}
I also threw in an extra defer for good measure. 😉 Run the code yourself to see that now the output is as follows:
baz is done
an extra defer for good measure
recovered 123
bar is done
foo is done
Two things to point out here. First, and most important, there’s no panic! The panic was recovered, and execution was permitted to continue as normal from the recovery point, without the program exiting. This is one other thing that functions like log.Fatal
, doesn’t allow—there’s no opportunity to recover from a log.Fatal
!
Second, notice the order of the deferred statement executions. They execute in the opposite order declared, even considering the recovery.
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)