Why did Go add panic and recover in addition to er

2020-07-05 05:16发布

Why did Go end up adopting exception handling with panic/recover, when the language is so idiomatic and a strong advocate of error codes? What scenarios did the designers of Go envision not handled by error codes and necessitate panic/recover?

I understand convention says limit panic/recover, but does the runtime also limit them in ways that they can't be used as general throw/catch in C++?

4条回答
beautiful°
2楼-- · 2020-07-05 05:37

Some history:

In the early days of Go (before version 1.0) there was no recover(). A call to panic() would terminate an application without any way to stop that.

I've found the original discussion that led to adding recover(), you can read it on the golang-nuts discussion forum:

Proposal for an exception-like mechanism

Beware: the discussion dates back to March, 25, 2010, and it is quite exhausting and long (150 posts through 6 pages).

Eventually it was added on 2010-03-30:

This release contains three language changes:

  1. The functions panic and recover, intended for reporting and recovering from failure, have been added to the spec:
    http://golang.org/doc/go_spec.html#Handling_panics
    In a related change, panicln is gone, and panic is now a single-argument function. Panic and recover are recognized by the gc compilers but the new behavior is not yet implemented.

Multi-return values and conventions provide a cleaner way to handle errors in Go.

That does not mean however that in some (rare) cases the panic-recover is not useful.

Quoting from the official FAQ: Why does Go not have exceptions?

Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.

Here is a "real-life" example for when/how it can be useful: quoting from blog post Defer, Panic and Recover:

For a real-world example of panic and recover, see the json package from the Go standard library. It decodes JSON-encoded data with a set of recursive functions. When malformed JSON is encountered, the parser calls panic to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'unmarshal' methods of the decodeState type in decode.go).

Another example is when you write code (e.g. package) which calls a user-supplied function. You can't trust the provided function that it won't panic. One way is not to deal with it (let the panic wind up), or you may choose to "protect" your code by recovering from those panics. A good example of this is the http server provided in the standard library: you are the one providing functions that the server will call (the handlers or handler functions), and if your handlers panic, the server will recover from those panics and not let your complete application die.

How you should use them:

The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.

Related and useful readings:

http://blog.golang.org/defer-panic-and-recover

http://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right

https://golang.org/doc/faq#exceptions

http://evanfarrer.blogspot.co.uk/2012/05/go-programming-language-has-exceptions.html

查看更多
疯言疯语
3楼-- · 2020-07-05 05:44

I think that your question is the result of a mental model you maintain which is instilled by popular mainstream languages such as Java, C++, C#, PHP and zillions of other which simply got exceptions wrong.

The thing is, exceptions per se are not a wrong concept but abusing them to handle cases which are, in reality, not exceptional is. My personal pet peeve is the filesystem handling API of Java (and .NET, which copied that of Java almost verbatim): why on Earth failure to open a file results in an exception if that file does not exist? The filesystem is an inherently racy medium, and is specified to be racy, so the only correct way to ensure a file exists before opening it for reading is to just open it and then check for "file does not exist" error: the case of the file not existing is not exceptional at all.

Hence Go clearly separates exceptional cases from plain normal errors. The motto of the stance Go maintains on handling errors is "Errors are values" and thus normal expected errors are handled as values, and panic() serves to handle exceptional cases. A good simple example:

  • An attempt to dereference a nil pointer results in a panic.

    The rationale: your code went on and tried to dereference a pointer which does not point to any value. The immediately following code clearly expects that value to be available—as the result of the dereferencing operation. Hence the control flow clearly can't proceed normally in any sensible way, and that's why this is an exceptional situation: in a correct program, dereferencing of nil pointers cannot occur.

  • The remote end of a TCP stream abruptly closed its side of the stream and the next attempt to read from it resulted in an error.

    That's pretty normal situation: one cannot sensbily expect a TCP session to be rock-solid: network outages, packet drops, unexpected power blackouts do occur, and we have to be prepared for unexpected stream closures by our remote peers.

A minor twist to panic() is that Go does not force you to blindly follow certain dogmas, and you can freely "abuse" panic/recover in tightly-controlled specific cases such as breaking out from a deeply-nested processing loop by panicking with an error value of a specific type known and checked for at the site performing recover.

Further reading:

查看更多
地球回转人心会变
4楼-- · 2020-07-05 05:56

I think the reason is concurrency model. Go is highly concurrent language in nature and core syntax. Scenario when some computation localized to concurrent process failure, but hole system continue to work can be viewed as normal. To my mind panic and recover are about failure handling, not exceptions.

查看更多
做个烂人
5楼-- · 2020-07-05 05:56

Because this is more powerful as it allows to create more resilient programs.

In C you can't recover from a NULL pointer dereference.

In Go you can catch it with recover.

This allows for example to create in Go an HTTP server that can catch a NULL pointer dereference when handling a request and return an error 500 instead of crashing the whole server.

查看更多
登录 后发表回答