Menu
Coddy logo textTech

Panic, Defer, and Recover

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 57 of 107.

While Go emphasizes returning errors, some situations are truly unrecoverable. For these cases, Go provides panic, defer, and recover—a mechanism for handling exceptional circumstances.

panic stops normal execution immediately. It's reserved for programming errors like accessing an out-of-bounds index, not for expected errors like invalid user input:

func MustGetConfig(key string) string {
    value, exists := config[key]
    if !exists {
        panic("missing required config: " + key)
    }
    return value
}

defer schedules a function to run when the surrounding function returns—whether normally or due to a panic. Deferred calls execute in reverse order (last-in, first-out):

func ProcessFile() {
    fmt.Println("Opening file")
    defer fmt.Println("Closing file")
    fmt.Println("Processing...")
    // Output: Opening file, Processing..., Closing file
}

recover catches a panic and returns normal execution. It only works inside a deferred function:

func SafeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

This pattern converts a panic into a regular error, allowing your program to continue. Use it sparingly—prefer returning errors for expected failure cases, and reserve panic/recover for truly exceptional situations like corrupted state or unrecoverable conditions.

challenge icon

Challenge

Easy

Let's build a safe division calculator that demonstrates how to use panic, defer, and recover to handle exceptional situations gracefully. Your calculator will attempt risky operations and convert panics into regular errors that can be handled normally.

You'll organize your code across two files:

  • calculator.go: Create the division logic with panic recovery.

    Implement a Divide function that takes two integers and panics with the message "division by zero" if the divisor is zero. Otherwise, it returns the integer result of the division.

    Implement a SafeDivide function that takes two integers and returns both an integer result and an error. This function should:

    • Use defer with an anonymous function to recover from any panic
    • If a panic is recovered, convert it to an error with the format "calculation error: [panic message]"
    • Call the Divide function internally
    • Return the result and nil if successful, or zero and the error if a panic was recovered
  • main.go: Read two integers from input and use SafeDivide to perform the calculation safely. Also demonstrate defer by printing cleanup messages in the correct order.

    Your main function should:

    • Print Starting calculation at the beginning
    • Use defer to schedule printing Cleanup complete
    • Use another defer to schedule printing Releasing resources
    • Call SafeDivide with the input values
    • Print either Result: [value] or Error: [error message] based on the outcome

The following inputs will be provided:

  • Line 1: First integer (dividend)
  • Line 2: Second integer (divisor)

Remember that deferred calls execute in reverse order (last-in, first-out), so your cleanup messages should appear in the opposite order from how they were deferred.

For example, given 20 and 4, your output should be:

Starting calculation
Result: 5
Releasing resources
Cleanup complete

And given 10 and 0, your output should be:

Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup complete

Cheat sheet

Go provides panic, defer, and recover for handling exceptional circumstances.

panic stops normal execution immediately and is reserved for unrecoverable programming errors:

func MustGetConfig(key string) string {
    value, exists := config[key]
    if !exists {
        panic("missing required config: " + key)
    }
    return value
}

defer schedules a function to run when the surrounding function returns. Deferred calls execute in reverse order (last-in, first-out):

func ProcessFile() {
    fmt.Println("Opening file")
    defer fmt.Println("Closing file")
    fmt.Println("Processing...")
    // Output: Opening file, Processing..., Closing file
}

recover catches a panic and returns normal execution. It only works inside a deferred function:

func SafeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered from panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

This pattern converts a panic into a regular error. Prefer returning errors for expected failures, and reserve panic/recover for truly exceptional situations.

Try it yourself

package main

import (
	"fmt"
)

func main() {
	// Read input
	var dividend, divisor int
	fmt.Scanln(&dividend)
	fmt.Scanln(&divisor)

	// Print starting message
	fmt.Println("Starting calculation")

	// TODO: Use defer to schedule "Cleanup complete" message

	// TODO: Use defer to schedule "Releasing resources" message
	// Remember: deferred calls execute in LIFO order (last-in, first-out)

	// TODO: Call SafeDivide with the input values

	// TODO: Print either "Result: [value]" or "Error: [error message]"
	// based on whether an error was returned
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming