Handling panics
…
The return value of
recover
isnil
when the goroutine is not panicking or recover was not called directly by a deferred function. Conversely, if a goroutine is panicking andrecover
was called directly by a deferred function, the return value ofrecover
is guaranteed not to benil
. To ensure this, callingpanic
with anil
interface value (or an untypednil
) causes a run-time panic.
As we saw before, you can pass any value, of an type, to the panic
function. Then, if that panic is recovered, that value is returned by the call to recover
:
func panicAndRecover() {
defer func() {
r := recover()
fmt.Printf("recovered value %v of type %T\n", r, r)
}()
panic(123)
}
Running this code will produce the following output:
recovered value 123 of type int
While you can panic with any value or type, it’s most common to panic with either a string, or an error.
The one special case, as mentioned, is the value nil
, either an untyped literal nil
, or a nil interface. The exception does not apply to nil maps, channels, or slices. If you panic with nil
or a nil interface, it will… get this… cause a run-time panic. LOL.
You can distinguish such a panic from the type that it produces. If we modify the above code:
func panicAndRecover() {
defer func() {
r := recover()
fmt.Printf("recovered value %v of type %T\n", r, r)
}()
panic(nil)
}
We can see that the paniced value is of type *runtime.PanicNilError
:
recovered value panic called with nil argument of type *runtime.PanicNilError
The
protect
function in the example below invokes the function argumentg
and protects callers from run-time panics raised byg
.func protect(g func()) { defer func() { log.Println("done") // Println executes normally even if there is a panic if x := recover(); x != nil { log.Printf("run time panic: %v", x) } }() log.Println("start") g() }
Quotes from The Go Programming Language Specification Language version go1.23 (June 13, 2024)