Panic and Recover

Panic

If you aim to achieve error handling behavior similar to other languages, you can use the panic function. Its purpose is to immediately terminate the application upon invocation while also reporting the error. This includes printing the error message, displaying a goroutine trace, and setting the application's exit status to a non-zero value.

func main() {
    _, err := os.Open("/tmp/nonexisting")
    if err != nil {
        panic(err) // panic: open /fp/nonexisting: no such file or directory
        fmt.Println("Follow The Pattern!")
    }
}

Run in your browser (opens in a new tab)

In the code snippet above, the panic function is used, passing the value of the err variable, which causes the program to panic if the err value is not nil. It's essential to note that after calling panic, further processing does not occur. The program exits immediately in this case.

The runtime can also trigger a panic if the situation requires it.

func main() {
    a := 1
    b := 0
 
    fmt.Println(a / b)
    fmt.Println("Follow The Pattern!") // won't be executed
}

Dividing by zero will result in a panic, causing the application to terminate with an error. The program assumes a fatal error has occurred and needs to be stopped as soon as possible.

Defer

In a typical Go application, expressions are executed sequentially, one after the other.

fmt.Println("Follow")
fmt.Println("The")
fmt.Println("Pattern!")

There are exceptions, such as defer. Functions marked with defer don't execute when they are called but instead run after the invoking function has finished. If multiple defer functions are used, their execution order will be the reverse of their calls.

func main() {
    defer fmt.Println("Follow")
    defer fmt.Println("The")
    defer fmt.Println("Pattern!")
    // Pattern!
    // The
    // Follow
}

The practical advantage of the defer keyword is that it allows you to use an initializing function and its associated closing logic closely together. Anonymous functions are often passed to defer because there's no need to define a new function explicitly. However, a function call must always follow the defer keyword.

package main
 
import (
	"fmt"
	"os"
)
 
func main() {
	f, err := os.Open("/tmp/fp.txt")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
		os.Exit(1)
	}
	defer func() {
		err := f.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "error: %v\n", err)
			os.Exit(1)
		}
	}()
	fmt.Println("start to write file")
	fmt.Fprintln(f, "Follow The Pattern")
}

In the code for creating a file, the f variable references the opened file. After writing to the file, it should be closed, which is done using the f.Close() method. The closing logic is placed within an anonymous function and called after the defer keyword. Placing the closing logic immediately after the file creation logic makes the code more readable.

Run this code in your browser (opens in a new tab) without installation

Recover

recover is a built-in function that regains control over a goroutine where a panic function was called. recover only works within a defer block. During normal execution, the recover function always returns nil and has no other side effects. If a panic occurs in the same goroutine where recover is called, it captures the error contained within the panic and stores it in the err variable.

package main
 
import "fmt"
 
func main() {
	fmt.Println("Learn coding")
	panicker()
	fmt.Println("Fast!")
}
 
func panicker() {
	fmt.Println("it will panic")
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("Error:", err)
		}
	}()
	panic("Fatal error")
	fmt.Println("this won't be printed")
}
 

You can run this code here (opens in a new tab).

The code snippet above demonstrates how the recover function can catch a panic for error handling. The recover function operates only within a defer block, capturing the error passed to it by the panic function and storing it in the err variable. The "end of panicker" message won't be printed because the execution stops at the panic line and continues within the defer block. It prints the error and exits the function, but the program execution continues, allowing the main function to run without issues.

The code snippet above demonstrates how the recover function can catch a panic for error handling. The recover function operates only within a defer block, capturing the error passed to it by the panic function and storing it in the err variable. The end of panicker message won't be printed because the execution stops at the panic line and continues within the defer block. It prints the error and exits the function, but program execution continues, allowing the main function to run without issues.

Try-Catch

In the Go programming language, there is no classical try-catch style error handling. The defer-recover pair can provide similar functionality, but this approach is not typically used in Go and is not meant to be used as a try-catch. Error handling in Go is primarily done using the error interface. This results in more transparent code because it distinguishes clearly between expected and unexpected errors. In other languages, without examining the error, it can be harder to figure out.