Go:
Handling errors

How to:

In Go, error handling is explicitly managed using the error type. Functions that can fail return an error as their last return value. Checking if this error value is nil will tell you if an error occurred.

package main

import (
    "errors"
    "fmt"
)

func Compute(value int) (int, error) {
    if value > 100 {
        return 0, errors.New("value must be 100 or less")
    }
    return value * 2, nil
}

func main() {
    result, err := Compute(150)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
    
    // Handling an error gracefully
    anotherResult, anotherErr := Compute(50)
    if anotherErr != nil {
        fmt.Println("Error:", anotherErr)
    } else {
        fmt.Println("Result:", anotherResult)
    }
}

Sample output for the above code:

Error: value must be 100 or less
Result: 100

In this example, Compute function either returns a calculated value or an error. The caller handles the error by checking if err is not nil.

Deep Dive

Go’s approach to error handling is deliberately straightforward and type-safe, requiring explicit checks of errors. This concept contrasts with exception-based error handling seen in languages like Java and Python, where errors are propagated up the call stack unless caught by an exception handler. The Go team argues that the explicit handling of errors results in clearer and more reliable code, as it forces programmers to address errors immediately where they occur.

However, some critiques mention that this pattern can lead to verbose code, especially in complex functions with many error-prone operations. In response, newer versions of Go have introduced more sophisticated error handling features, such as error wrapping, making it easier to provide context to an error without losing the original error information. The community has also seen proposals for new error handling mechanisms, such as check/handle, although these remain under discussion as of my last update.

Go’s error handling philosophy emphasizes understanding and planning for errors as part of the program’s normal flow. This approach encourages the development of more resilient and predictable software, albeit with a potential increase in boilerplate code. Alternative patterns and libraries exist to streamline error handling for particularly complex cases, but Go’s built-in error type remains the foundation of error handling in the language.