Go - Defer, Panic, and Recover
Error handling is crucial for writing robust and maintainable code in Go. It provides three key tools:
- defer: Delays function execution until the surrounding function completes.
- panic: Immediately stops the program when a critical error occurs.
- recover: Regains control after a panic to prevent a crash.
Go treats errors as values, usually returned as the last function result. When an error is too severe, panic and recover help manage unexpected failures.
1. defer
in Go
The defer
statement is used to ensure that a function call is executed later in the program’s execution, usually for cleanup purposes. This is particularly useful for releasing resources like file handles, database connections, or mutex locks.
How defer
Works?
When you use defer
, the deferred function is pushed onto a stack. When the surrounding function returns, the deferred functions are executed in Last In, First Out (LIFO) order.
Example:
package main
import "fmt"
func main() {
// defer the execution of Println() function
defer fmt.Println("Three")
fmt.Println("One")
fmt.Println("Two")
}
Output:
One
Two
Three
In this example, the fmt.Println("Three")
statement is deferred, so it executes after fmt.Println("One")
and fmt.Println("Two")
.
Multiple defer
Statements
When multiple defer
statements are used, they are executed in LIFO order.
Example:
package main
import "fmt"
func main() {
defer fmt.Println("One")
defer fmt.Println("Two")
defer fmt.Println("Three")
}
Output:
Three
Two
One
Here, the last defer
statement (fmt.Println("Three")
) is executed first, and the first defer
statement (fmt.Println("One")
) is executed last.
2. Golang panic
The panic
statement is used to immediately stop the execution of a program when an unrecoverable error occurs. When a panic
is triggered, the program starts unwinding the stack, running any deferred functions along the way, and then crashes with an error message.
How panic
Works?
When panic
is called, the normal flow of the program is interrupted. The program prints a panic message, followed by a stack trace, and then terminates.
Example: Basic panic
Usage
package main
import "fmt"
func main() {
fmt.Println("Help! Something bad is happening.")
panic("Ending the program")
fmt.Println("Waiting to execute") // This line will not execute
}
Output:
Help! Something bad is happening.
panic: Ending the program
In this example, the program terminates when it encounters the panic
statement, so the line fmt.Println("Waiting to execute")
is never executed.
Example:
Let’s look at a more practical example where panic
is used to handle division by zero.
package main
import "fmt"
func division(num1, num2 int) {
// if num2 is 0, program is terminated due to panic
if num2 == 0 {
panic("Cannot divide a number by zero")
} else {
result := num1 / num2
fmt.Println("Result: ", result)
}
}
func main() {
division(4, 2)
division(8, 0) // This will cause a panic
division(2, 8) // This line will not execute
}
Output:
Result: 2
panic: Cannot divide a number by zero
Here, the program panics when it attempts to divide by zero, and the subsequent function call (division(2, 8)
) is never executed.
3. Recover
While panic
is used to terminate a program, recover
is used to regain control after a panic
has occurred. This allows the program to handle the error gracefully instead of crashing.
The recover
function is used inside a deferred function to catch the panic message and resume normal execution. If recover
is called outside of a deferred function, it has no effect.
Example: Using recover
to Handle panic
package main
import "fmt"
// recover function to handle panic
func handlePanic() {
// detect if panic occurs or not
a := recover()
if a != nil {
fmt.Println("RECOVER:", a)
}
}
func division(num1, num2 int) {
// execute the handlePanic even after panic occurs
defer handlePanic()
// if num2 is 0, program is terminated due to panic
if num2 == 0 {
panic("Cannot divide a number by zero")
} else {
result := num1 / num2
fmt.Println("Result:", result)
}
}
func main() {
division(4, 2)
division(8, 0) // This will cause a panic, but it will be recovered
division(2, 8)
}
Output:
Result: 2
RECOVER: Cannot divide a number by zero
Result: 0
In this example, the handlePanic
function is deferred inside the division
function. When a panic occurs, handlePanic
is called, and the program recovers from the panic instead of terminating.
Key Points About recover
- Deferred Execution:
recover
only works inside deferred functions. This ensures that it is called during the stack unwinding process after a panic. - Panic Message: The
recover
function returns the value passed topanic
, allowing you to log or handle the error message. - Graceful Recovery: Using
recover
allows your program to continue executing even after a critical error.
When to Use defer
, panic
, and recover
defer
: Usedefer
for cleanup tasks, such as closing files, releasing locks, or logging. It ensures that resources are properly managed, even if an error occurs.panic
: Usepanic
for unrecoverable errors, such as invalid input or system failures, where continuing execution would cause more harm.recover
: Userecover
to handle panics gracefully, especially in long-running applications like servers, where crashing is not an option.
Conclusion
Go’s defer, panic, and recover help manage errors and resource cleanup effectively:
- Use defer for cleanup and releasing resources.
- Use panic for critical errors that should stop execution.
- Use recover to handle panics and prevent crashes.
By mastering these, you can write robust and maintainable Go programs.