The error interface, wrapping with %w, errors.Is / errors.As.
The error Interface
Go doesn't have exceptions. Instead, functions that can fail return an extra
error value, and the caller checks it. This sounds tedious but makes failure
explicit and inescapable in your code.
The interface
The built-in error is just:
type error interface {
Error() string
}
Any type with a Error() string method is an error.
Creating errors
import "errors"
err := errors.New("not found")
err := fmt.Errorf("user %d not found", id) // formatted
errors.New is the simplest. fmt.Errorf is what you reach for when the message
needs values from variables.
The classic check-and-return
Custom error types
Sometimes you need an error that carries extra data — say, a status code, or a
field name. Define a struct with an Error() method:
A caller can type-assert this back into *ValidationError to access Field —
or do that with errors.As, which we'll see next.
The golden rule
Don't ignore errors. Either handle them, or wrap and return them.
The pattern if err != nil { return ... } shows up everywhere. Embrace the noise —
each line is the language pointing at a place where something can break.
Sentinel errors
A sentinel is a package-level error you compare against. The standard
library exports plenty:
import "io"
if errors.Is(err, io.EOF) { // end of stream
break
}
You can define your own:
package shop
var ErrOutOfStock = errors.New("out of stock")
Callers then check with errors.Is(err, shop.ErrOutOfStock). We cover errors.Is
next lesson.