Go Error Handling
In Go, errors are values. Instead of using "exceptions" (like try/catch in Java or Python), Go functions return an error as their last return value.
Basic Error Handling
The error type is a built-in interface. If a function succeeds, it returns nil for the error.
| package main
import (
"errors"
"fmt"
)
func f(arg int) (int, error) {
if arg == 42 {
// Return a simple error using errors.New
return -1, errors.New("can't work with 42")
}
return arg + 3, nil
}
func main() {
result, err := f(42)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
|
Custom Errors
You can create your own error types by implementing the Error() method. This is useful for passing extra data back to the caller.
| package main
import "fmt"
type argError struct {
arg int
prob string
}
func (e *argError) Error() string {
return fmt.Sprintf("%d - %s", e.arg, e.prob)
}
func f(arg int) (int, error) {
if arg == 42 {
return -1, &argError{arg, "can't work with it"}
}
return arg + 3, nil
}
func main() {
_, err := f(42)
if ae, ok := err.(*argError); ok {
fmt.Println(ae.arg) // Access custom fields
fmt.Println(ae.prob)
}
}
|
Error Wrapping (%w)
In modern Go (1.13+), you can "wrap" an error to add more context while keeping the original error visible.
| func doSomething() error {
err := originalError()
return fmt.Errorf("extra context: %w", err)
}
// To check for a specific error in a chain:
if errors.Is(err, ErrNotFound) {
// ...
}
|
Why handle errors explicitly?
- Safety: You are forced to think about what happens when things go wrong.
- No Surprises: Control flow is clear; there are no hidden jumps (exceptions).
- Better Debugging: Error chains give you a clear "trail" of what failed and why.