Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `defer recover()` not catch panics?

Tags:

Why does a call to defer func() { recover() }() successfully recover a panicking goroutine, but a call to defer recover() not?

As an minimalistic example, this code doesn't panic

package main  func main() {     defer func() { recover() }()     panic("panic") } 

However, replacing the anonymous function with recover directly panics

package main  func main() {     defer recover()     panic("panic") } 
like image 214
jinpan Avatar asked Apr 08 '15 14:04

jinpan


People also ask

Does defer work with Panic?

In Go, we use defer, panic and recover statements to handle errors. We use defer to delay the execution of functions that might cause an error. The panic statement terminates the program immediately and recover is used to recover the message during panic.

What happens when a Goroutine panics?

A panic stops the normal execution of a goroutine: When a program panics, it immediately starts to unwind the call stack. This continues until the program crashes and prints a stack trace, or until the built-in recover function is called.

How do you capture panic in Golang?

The Go paradigm is to check for errors explicitly. A program should only panic if the circumstances under which it panics do not happen during ordinary program executing. For instance, not being able to open a file is something that can happen and should not cause a panic while running out of memory is worth a panic.

Can you defer Goroutine?

The defer keyword and the deferred function call together form a defer statement. Like goroutine function calls, all the results of the function call (if the called function has return results) must be discarded in the function call statement.


2 Answers

Quoting from the documentation of the built-in function recover():

If recover is called outside the deferred function it will not stop a panicking sequence.

In your second case recover() itself is the deferred function, and obviously recover() does not call itself. So this will not stop the panicking sequence.

If recover() would call recover() in itself, it would stop the panicking sequence (but why would it do that?).

Another Interesting Example:

The following code also doesn't panic (try it on the Go Playground):

package main  func main() {     var recover = func() { recover() }     defer recover()     panic("panic") } 

What happens here is we create a recover variable of function type which has a value of an anonymous function calling the built-in recover() function. And we specify calling the value of the recover variable to be the deferred function, so calling the builtin recover() from that stops the panicing sequence.

like image 60
icza Avatar answered Oct 05 '22 03:10

icza


The Handling panic section mentions that

Two built-in functions, panic and recover, assist in reporting and handling run-time panics

The recover function allows a program to manage behavior of a panicking goroutine.

Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing.

When the running of deferred functions reaches D, the return value of D's call to recover will be the value passed to the call of panic.
If D returns normally, without starting a new panic, the panicking sequence stops.

That illustrates that recover is meant to be called in a deferred function, not directly.
When it panic, the "deferred function" cannot be the built-in recover() one, but one specified in a defer statement.

DeferStmt = "defer" Expression . 

The expression must be a function or method call; it cannot be parenthesized.
Calls of built-in functions are restricted as for expression statements.

With the exception of specific built-in functions, function and method calls and receive operations can appear in statement context.

like image 23
VonC Avatar answered Oct 05 '22 01:10

VonC