Go’s type system catches many mistakes at compile time, but others, like an out-of-bounds array access or nil pointer dereference, require checks at run time. When the Go runtime detects these mistakes, it panics.
During a typical panic, normal execution stops, all deferred function calls in that goroutine are executed, and the program crashes with a log message. This log message includes the panic value, which is usually an error message of some sort, and, for each goroutine, a stack trace showing the stack of function calls that were active at the time of the panic. This log message often has enough information to diagnose the root cause of the problem without running the program again, so it should always be included in a bug report about a panicking program.
Not all panics come from the runtime. The built-in panic
function
may be called directly; it accepts any value as an argument. A panic
is often the best thing to do when some “impossible” situation
happens, for instance, execution reaches a case that
logically can’t happen:
switch s := suit(drawCard()); s { case "Spades": // ... case "Hearts": // ... case "Diamonds": // ... case "Clubs": // ... default: panic(fmt.Sprintf("invalid suit %q", s)) // Joker? }
It’s good practice to assert that the preconditions of a function hold, but this can easily be done to excess. Unless you can provide a more informative error message or detect an error sooner, there is no point asserting a condition that the runtime will check for you.
func Reset(x *Buffer) { if x == nil { panic("x is nil") // unnecessary! } x.elements = nil }
Although Go’s panic mechanism resembles exceptions in other
languages, the situations in which panic is used are quite different.
Since a panic causes the program to crash, it is generally used for grave
errors, such as a logical inconsistency in the program; diligent
programmers consider any crash to be proof of a bug in their code. In a
robust program, “expected” errors, the kind that arise from incorrect
input, misconfiguration, or failing I/O, should be handled gracefully; they
are best dealt with using error
values.
Consider the function regexp.Compile
, which compiles a regular
expression into an efficient form for matching.
It returns an error
if called with an ill-formed pattern, but
checking this error is unnecessary and burdensome if the caller knows
that a particular call cannot fail.
In such cases, it’s reasonable for the caller to handle an error by
panicking, since it is believed to be impossible.
Since most regular expressions are literals in the program source
code, the regexp
package provides a wrapper
function regexp.MustCompile
that does this check:
package regexp func Compile(expr string) (*Regexp, error) { /* ... */ } func MustCompile(expr string) *Regexp { re, err := Compile(expr) if err != nil { panic(err) } return re }
The wrapper function makes it convenient for clients to initialize a package-level variable with a compiled regular expression, like this:
var httpSchemeRE = regexp.MustCompile(`^https?:`) // "http:" or "https:"
Of course, MustCompile
should not be called with untrusted input values.
The Must
prefix is a common naming convention for functions of
this kind, like template.Must
in Section 4.6.
When a panic occurs, all deferred functions are run in reverse order,
starting with those of the topmost function on the stack and
proceeding up to main
, as the program below demonstrates:
func main() { f(3) } func f(x int) { fmt.Printf("f(%d)\n", x+0/x) // panics if x == 0 defer fmt.Printf("defer %d\n", x) f(x - 1) }
When run, the program prints the following to the standard output:
f(3) f(2) f(1) defer 1 defer 2 defer 3
A panic occurs during the call to f(0)
, causing the three
deferred calls to fmt.Printf
to run.
Then the runtime terminates the program, printing the panic message
and a stack dump to the standard error stream (simplified for clarity):
panic: runtime error: integer divide by zero main.f(0) src/gopl.io/ch5/defer1/defer.go:14 main.f(1) src/gopl.io/ch5/defer1/defer.go:16 main.f(2) src/gopl.io/ch5/defer1/defer.go:16 main.f(3) src/gopl.io/ch5/defer1/defer.go:16 main.main() src/gopl.io/ch5/defer1/defer.go:10
As we will see soon, it is possible for a function to recover from a panic so that it does not terminate the program.
For diagnostic purposes, the runtime
package lets the
programmer dump the stack using the same machinery.
By deferring a call to printStack
in main
,
func main() { defer printStack() f(3) } func printStack() { var buf [4096]byte n := runtime.Stack(buf[:], false) os.Stdout.Write(buf[:n]) }
the following additional text (again simplified for clarity) is printed to the standard output:
goroutine 1 [running]: main.printStack() src/gopl.io/ch5/defer2/defer.go:20 main.f(0) src/gopl.io/ch5/defer2/defer.go:27 main.f(1) src/gopl.io/ch5/defer2/defer.go:29 main.f(2) src/gopl.io/ch5/defer2/defer.go:29 main.f(3) src/gopl.io/ch5/defer2/defer.go:29 main.main() src/gopl.io/ch5/defer2/defer.go:15
Readers familiar with exceptions in other languages may be
surprised that runtime.Stack
can print information about functions that
seem to have already been “unwound.” Go’s panic mechanism runs the
deferred functions before it unwinds the stack.