Go by Example: Error Handling
Learn idiomatic Error Handling in Go. Unlike exception-based languages, Go treats errors as values. This example demonstrates the standard pattern of returning `(result, error)`, checking for non-nil errors, and defining custom error types for richer context.
Code
package main
import (
"errors"
"fmt"
)
// By convention, errors are the last return value and have type error, a built-in interface.
func f1(arg int) (int, error) {
if arg == 42 {
// errors.New constructs a basic error value with the given error message.
return -1, errors.New("can't work with 42")
}
// A nil value in the error position indicates that there was no error.
return arg + 3, nil
}
// Custom error type
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f2(arg int) (int, error) {
if arg == 42 {
// Return custom error
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
// Basic error handling
for _, i := range []int{7, 42} {
if r, e := f1(i); e != nil {
fmt.Println("f1 failed:", e)
} else {
fmt.Println("f1 worked:", r)
}
}
// Using custom error data
_, e := f2(42)
if ae, ok := e.(*argError); ok {
fmt.Println(ae.arg)
fmt.Println(ae.prob)
}
}Explanation
In Go, it's idiomatic to communicate errors via an explicit, separate return value. This contrasts with the exceptions used in languages like Java and Ruby and the overloaded single result / error value sometimes used in C.
Go's approach makes it easy to see which functions return errors and to handle them using the same language constructs employed for any other, non-error tasks. The error type is a built-in interface. A nil error denotes success; a non-nil error denotes failure.
Benefits of explicit error handling:
- Control flow is visible and explicit
- No hidden control paths (like uncaught exceptions)
- Errors are values that can be programmed with
- Encourages handling errors where they occur

