Go Errors
Purpose
Go treats errors as ordinary values rather than exceptions. Functions signal failure by returning an error as their last return value; callers check it explicitly. Go also uses iota-based const blocks as a lightweight substitute for enums, often combined with error types.
Implementation Notes
The error Interface
type error interface {
Error() string
}Any type that implements Error() string satisfies the interface.
Idiomatic Error Handling
Return error as the last value; return zero values for all other results on failure:
i, err := strconv.Atoi("42b")
if err != nil {
fmt.Println("couldn't convert:", err)
return
}
// i is valid hereCreating Errors
// simple string error
err := errors.New("something went wrong")
// formatted error
err := fmt.Errorf("user %s not found", name)
// wrapping — %w preserves the original error for errors.Is / errors.As
err := fmt.Errorf("failed to get user: %w", originalErr)Custom Error Types
Implement the error interface with a struct to carry structured data:
type userError struct {
name string
}
func (e userError) Error() string {
return fmt.Sprintf("%v has a problem with their account", e.name)
}
func sendSMS(msg, userName string) error {
if !canSendToUser(userName) {
return userError{name: userName}
}
// ...
return nil
}Iota — Pseudo-Enum Constants
Go has no native enum type. The closest idiom combines a type definition with a const block using iota:
type sendingChannel int
const (
Email sendingChannel = iota // 0
SMS // 1
Phone // 2
)iota resets to 0 at the start of each const block and increments by 1 per constant. For string-based pseudo-enums:
type sendingChannel string
const (
Email sendingChannel = "email"
SMS sendingChannel = "sms"
Phone sendingChannel = "phone"
)
func sendNotification(ch sendingChannel, message string) { /* ... */ }The type definition prevents accidental use of a plain string (the compiler rejects untyped string variables), though explicit conversion and string literals are still accepted.
Trade-offs
- No exhaustiveness checking — unlike Rust’s
Resultor TypeScript union types, Go does not force callers to handle errors. A careless caller can ignore the error return entirely. - Wrapping vs. hiding — use
%wwhen callers may need to inspect the underlying error witherrors.Is/errors.As; use%v(orerrors.New) when the original error is an implementation detail. - Iota is just integers —
iotaconstants are not sealed; any value of the underlying type can be cast to the pseudo-enum type, so invalid values are possible at runtime.
References
Links
- go_interfaces — the
errorinterface is a standard Go interface - go_functions — multiple return values
- go_index