panic and recover
panic(value)
defer func() {
if r := recover(); r != nil {
// handle panic
}
}()
Triggering a panic
Calling panic(value) immediately stops the current function's normal execution and begins unwinding the stack, running any deferred calls along the way, until either something recovers or the program crashes.
Example: Triggering a panic
// Every Go file belongs to a package; main builds an executable
package main
// Import the fmt package
import "fmt"
// Define the function mustBePositive
func mustBePositive(n int) int {
// Check whether n < 0
if n < 0 {
panic("negative number not allowed")
}
// Send n back to the caller
return n
}
// main is where the program starts running
func main() {
// defer runs this call when the surrounding function returns
defer fmt.Println("this still runs during unwind")
// Print the values followed by a newline
fmt.Println(mustBePositive(5))
}
Login to try C/C++/Java/PHP code in the editor
Recovering from a panic
recover, called from within a deferred function, stops the panic from propagating further and returns the value passed to panic -- allowing the program to log the problem and keep running instead of crashing entirely.
Example: Recovering from a panic
// Every Go file belongs to a package; main builds an executable
package main
// Import the fmt package
import "fmt"
// Define the function safeDivide
func safeDivide(a, b int) (result int) {
// defer runs this call when the surrounding function returns
defer func() {
// Check whether r := recover(); r != nil
if r := recover(); r != nil {
// Print the values followed by a newline
fmt.Println("recovered from:", r)
result = 0
}
}()
// Send a / b back to the caller
return a / b
}
// main is where the program starts running
func main() {
fmt.Println(safeDivide(10, 0))
fmt.Println("program continues normally")
}
Login to try C/C++/Java/PHP code in the editor
panic vs Returning an Error
Idiomatic Go reserves panic for programmer errors or truly unrecoverable situations (like a required invariant being violated), while ordinary, expected failure conditions -- a missing file, invalid input -- should be communicated through a returned error instead.
Example: panic vs Returning an Error
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero") // expected failure: use error
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("handled gracefully:", err)
return
}
fmt.Println(result)
}
Login to try C/C++/Java/PHP code in the editor
- Using panic for ordinary, expected error conditions instead of returning an error -- panic is reserved for truly exceptional situations.
- Calling recover() outside of a deferred function, where it has no effect and always returns nil.
- Forgetting that recover only stops the panic in the goroutine where it's called -- it can't recover a panic happening in a different goroutine.
- panic immediately stops normal execution and starts unwinding the call stack, running deferred calls along the way.
- recover, called inside a deferred function, stops a panic in progress and lets the program continue.
- panic/recover is reserved for truly exceptional situations -- not a substitute for normal error returns.
- An unrecovered panic crashes the whole program with a stack trace.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: