defer recover()

674 views
Skip to first unread message

Matt Joiner

unread,
Nov 16, 2014, 5:23:19 AM11/16/14
to golan...@googlegroups.com
The following snippet doesn't work as I'd expect:

func main() {
    defer recover()
    panic("oh crap")
}

panic: oh crap

Yet this does work:

func main() {
    defer func() { recover() }()
    panic("oh crap")
}

Presumably it has something to do with recover() being a built-in. Is this mentioned in the spec somewhere? It doesn't seem intuitive.

Jan Mercl

unread,
Nov 16, 2014, 5:31:07 AM11/16/14
to Matt Joiner, golang-nuts
See the specs: http://golang.org/ref/spec#Handling_panics

Third bullet applies to the first version of the program:

- recover was not called directly by a deferred function.

In

defer recover()

recover itself is the deferred function.

-j

Kevin Gillette

unread,
Nov 16, 2014, 5:03:04 PM11/16/14
to golan...@googlegroups.com, anac...@gmail.com
If there isn't one already, there should be a `go vet` rule for this. Recover can't be used as a first class function (run http://play.golang.org/p/7fi5hK7HwB) even though it's not technically "generic", recover can only be called statically, and there's no useful case I can think of in which recover could be usefully deferred directly.

Andrew Gerrand

unread,
Nov 16, 2014, 5:17:12 PM11/16/14
to Kevin Gillette, golan...@googlegroups.com, anac...@gmail.com
It's the kind of mistake you make once and then never again, so it's probably not worth putting into go vet.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Matt Joiner

unread,
Nov 16, 2014, 7:09:31 PM11/16/14
to Andrew Gerrand, Kevin Gillette, golang-nuts
Why does the spec specify this behaviour?

Ian Taylor

unread,
Nov 16, 2014, 8:36:05 PM11/16/14
to Matt Joiner, Andrew Gerrand, Kevin Gillette, golang-nuts
On Sun, Nov 16, 2014 at 4:08 PM, Matt Joiner <anac...@gmail.com> wrote:
>
> Why does the spec specify this behaviour?

It's important that recover only work when called directly from a
deferred function, so that a deferred function can itself call a
function that uses a deferred recover. That is, given this code

func g() bool {
defer func() {
if x := recover(); x != nil {
return false
}
}
return someFunction()
}

func f() {
defer func() {
g()
}
panic(1)
}

the recover in g's deferred function must not pick up the panic in f.
If it did, it would be impossible for an independent package to
reliably use panic/recover in a controlled way.

Given that, we see that recover should only return non-nil if its
caller is a deferred function. When you use
defer recover()
the caller of recover is not a deferred function. So that use of
recover always returns nil.

It's true that we could probably make "defer recover()" a special
case, but there is no strong reason to do so. Better to be
consistent.

Ian

Matt Joiner

unread,
Nov 16, 2014, 9:41:04 PM11/16/14
to Ian Taylor, Andrew Gerrand, Kevin Gillette, golang-nuts
Thanks Ian.

gopanic

unread,
Dec 23, 2015, 11:55:17 AM12/23/15
to golang-nuts, anac...@gmail.com, a...@golang.org, extempor...@gmail.com
Hi!
I'm new in golang. And this thing with defer recover() blows my mind. I still do not understand - why this behaviour is built into the language?

I've found your(?) blog post http://www.airs.com/blog/archives/376
You wrote

 Otherwise it would be difficult for a deferred function to call a function which uses panic and recoverfor error handling; the recover might pick up the panic for its caller, which would be confusing.

So what? It's regular situation when something goes not like programmer expects, or when programmer makes mistakes.

I've spent few hours already trying to find answers for following questions:

How it works internally?
Why programmer restricted to write func() { recover() } - is it design decision only? or maybe it caused by lang desing (f.i. it's very difficult/inefficient to implement support of defer recover())? - why defer recover() doesn't work at all (even with unwanted behavior)?

PS
Somewhere I've read that reason is in backwards compatibility - is it so?


Ian Lance Taylor

unread,
Dec 23, 2015, 1:06:19 PM12/23/15
to gopanic, golang-nuts, Matt Joiner, Andrew Gerrand, Kevin Gillette
On Wed, Dec 23, 2015 at 1:44 AM, gopanic <kmak...@gmail.com> wrote:
>
> I'm new in golang. And this thing with defer recover() blows my mind. I
> still do not understand - why this behaviour is built into the language?

Because it seems like the best approach.


> I've found your(?) blog post http://www.airs.com/blog/archives/376
> You wrote
>
>> Otherwise it would be difficult for a deferred function to call a
>> function which uses panic and recoverfor error handling; the recover might
>> pick up the panic for its caller, which would be confusing.
>
>
> So what? It's regular situation when something goes not like programmer
> expects, or when programmer makes mistakes.

The record function has to work one way or another. This way seems
good, for the reasons I stated. Why would it be better if recover
worked in some other way?


> I've spent few hours already trying to find answers for following questions:
>
> How it works internally?

The blog post you already found explains how it works in gccgo. In
gc, see runtime/panic.go. I'm happy to answer more specific
questions, but it's hard to answer a general one like this.

> Why programmer restricted to write func() { recover() } - is it design
> decision only? or maybe it caused by lang desing (f.i. it's very
> difficult/inefficient to implement support of defer recover())? - why defer
> recover() doesn't work at all (even with unwanted behavior)?

defer recover() works as documented. It doesn't do anything very
useful, but it works.

It's not a case that's worth worrying about, because a program that
uses defer func() { recover() }() is not a well written program. You
should never just discard an ongoing panic without even logging it.
There is no point to making the semantics of defer more complex in
order to make defer recover() act like defer func() { recover() }(),
since nobody should be writing either case anyhow.

> PS
> Somewhere I've read that reason is in backwards compatibility - is it so?

No. It was designed to work this way from the start. Since as far as
I know Go invented defer/recover, there is nothing to be backward
compatible with.

Ian

gopanic

unread,
Dec 24, 2015, 3:56:04 AM12/24/15
to golang-nuts, kmak...@gmail.com, anac...@gmail.com, a...@golang.org, extempor...@gmail.com
Ian, thank you for the helpful answer
To clarify: while reading golang-book, first time I was confused after finding out that ?: is absent in Go, and when it happened second time with defer recover - it was like a "wtf? why some func's have `special` behaviour?" now I lean to thought that it's design decisions and it's OK

Thank

середа, 23 грудня 2015 р. 20:06:19 UTC+2 користувач Ian Lance Taylor написав:
Reply all
Reply to author
Forward
0 new messages