Panic and Recover

Hey! If you love Go and building Go apps as much as I do, let's connect on Twitter or LinkedIn. I talk about this stuff all the time!

Want to learn how to build better Go applications faster and easier? You can.

Check out my course on the Go Standard Library. You can check it out now for free.


Handling Errors Like a Pro: Understanding “Panic and Recover” in Go

Go is known for its simplicity and clear syntax. It’s also designed to be robust and handle errors gracefully. In this context, “gracefully” means preventing your program from crashing when something unexpected happens.

When things go wrong in Go, you don’t want your program to just stop working. You want it to handle the problem in a way that doesn’t cause errors for the user.

How it Works

Go uses two main concepts for handling errors:

  • panic: This is like a “panic button” for your code. When something really bad happens, like trying to access a file that doesn’t exist or dividing by zero, you can use panic to stop the program and potentially handle the error elsewhere. It’s important to remember that panic is meant for exceptional situations where a normal error-handling mechanism isn’t enough.

  • “Error Recovery” using recover: This function is used within a “defer” statement (which we’ll discuss later). Think of it as a way to catch the program before it crashes and handle the problem gracefully.

A Typical Example: Handling File Errors

Let’s say you have code that tries to read a file:

package main

import (
	"fmt"
	"os"
)

func main() {
	// Open the file for reading
	file, err := os.Open("myfile.txt")
	if err != nil {
		fmt.Println("Error opening file:", err) // Print the error message
		return // This will stop the program
	}
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered from error:", r)
			// You can access the error value and handle it
			// The `panic` keyword is used to raise an error
			// If a function panics, the program stops running.

			// Recoverable panic example
			if err := recover(); err == nil {
				fmt.Println("File opened successfully.")
				// Process the file content here
			} else {
				fmt.Println("An error occurred:", err)
			}

		}
	}()

	// ... (Code to handle the file)

	// Example:

	file = openFile("myfile.txt")
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Error opening file:", r)
		}
	}()

	// Recover from a potential error in the `openFile` function
	func openFile(filename string) (string, error) {
		return "myfile.txt"
	}
}

This Go code demonstrates how to use the recover function to handle an error that might occur when opening a file.

Explanation:

  • The panic function is used to stop the program immediately if there’s a problem accessing the file. It’s like pressing a panic button because something unexpected happened.
  • The code in the “recovered” section shows how you can use a defer statement and check for errors within it. This approach allows for handling potential issues with the file during opening without interrupting the program flow before the file is opened.

Typical Mistakes Beginners Make

A common mistake beginners make with panics is using them for regular error handling.

Remember:

  • Use panic judiciously: Panics are a powerful tool, but they should only be used when there’s a critical error that makes it impossible to continue the program’s execution.**

  • Use the defer function: This is a key concept in Go’s “panic and recover” system.

  • Use error handling for expected errors: Use regular error-handling techniques (like returning errors) for errors you expect might occur during normal program execution, such as invalid input or network issues.

Error Handling: The defer Approach

The code above uses the error keyword to handle unexpected file errors.

Here’s how it works:

  • “Panic and recover” functions:

    • When you use panic inside a defer function, it helps prevent your program from crashing.
    • The recover function is used in conjunction with panic. It’s used to capture errors that are raised within the defer block.
  • Handling errors:
    Go’s defer keyword lets you schedule code to run when a function exits, regardless of how it exits (e.g., normally or through an error).

How to use “panic and recover”:

func main() {
	// ... (Code to open a file)

	// Recover from a potential error in the `defer` function
	file := ""
	
	// This is an example of how to handle errors in a `defer` block.

	defer func() {
		if err := recover(); err != nil {
			fmt.Println("An error occurred, but we're handling it:", err)
		}
	}()
	
	if r := recover(); r != nil {
		fmt.Println("Recovered from error:", r)
	} else {
		// File opened successfully
		fmt.Println("File is open and ready to use!")

 
  • Best Practices:

  • Use panic for the unexpected: In Go, errors are handled by the function error. Let’s see how we can modify this code to handle both expected and unexpected errors:

package main

import (
    "fmt"
    "os"
)

func main() {
    defer func(filename string) {
        if err := recover(); err != nil {
            fmt.Println("An error occurred, while processing", filename)
        } 
    }(
   // Capture the error and pass it to the "panic" function for handling
	defer func() {
		if r := `error`; recover(); r != nil {
			fmt.Println("Error encountered:", r)
            // Handle the error if one occurred

		}()

Explanation:

  • This code snippet demonstrates how to use a recover function, which is crucial for effective “panic and recovery” in Go.

  • The defer function allows you to define code that runs after the function it’s in finishes execution, even if there’s an error.

Using defer with File Handling

The defer keyword is used for normal file handling. You can use it as a “panic” mechanism

Let’s say you want to read data from a file and ensure the file is closed

after it’s done, regardless of any errors:

package main

import (
	"fmt"
	"os"
)

func main() {
	file := `openFile`("myfile.txt") 
    // Handle normal file opening and closing logic in the "myfile.txt" code

	if err := r; recover();  != nil {
		fmt.Println("Error 'myfile' occurred, but we handled it!")
        // ... (Code to handle a potential error)
	}
}

Understanding the Code:

  • defer: This keyword is used before a function call. It indicates that the function should be executed after the current function returns.
  • “panic” and “recover” function: The defer keyword,

when used with a function, is a way to implement a “cleanup” action.

Important Notes:

  • In Go, errors are considered part of the normal flow of execution.

  • Errors can be handled gracefully using the “panic and



Stay up to date on the latest in Coding for AI and Data Science

Intuit Mailchimp