Go treats an operation failure as a value to return and handle, rather than an exception. A function can return both its result and an error; the caller checks the error before using the result. This makes the path where something goes wrong explicit. section 1.3.1 section 5.3.2
Read a word corpus
ReadCorpus(path) returns ([]string, error). If os.ReadFile fails, it returns nil and wraps the failure with the path: fmt.Errorf("unable to open %q for reading: %w", path, err). An empty file returns nil and ErrCorpusIsEmpty; a successful read returns the words and nil. Tests check both the word count and the error. section 5.3.2
A recognizable failure can be a sentinel error. ErrCorpusIsEmpty is a constant of a custom string type whose Error() method implements the error interface. This gives callers a stable value to check without exposing a mutable global error variable. Wrapping with %w preserves an underlying error for inspection. section 5.3.2 section 6.2.4
Choose the check that answers the caller’s question:
- Use errors.Is to recognize a particular failure, such as ErrTimeout or ErrInvalidDecimal, including a wrapped error. section 6.6.2 section 6.2.4
- Use errors.As when behavior depends on an error’s type. For an HTTP request failure, extract *url.Error and call its Timeout() method. section 6.6.2
Failure handling also shapes the boundary presented to clients. When an endpoint normally returns JSON, format its errors as JSON too—for example, {"error":"game over"}—so clients can use the same decoder regardless of response status. section 8.6.4
Listing 5.32 corpus.go: ReadCorpus method
const ErrCorpusIsEmpty = corpusError("corpus is empty")
// ReadCorpus reads the file located at the given path
// and returns a list of words.
func ReadCorpus(path string) ([]string, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("unable to open %q for reading: %w",
path, err)
}
if len(data) == 0 {
return nil, ErrCorpusIsEmpty
}
// we expect the corpus to be a line- or space-separated
// list of words
words := strings.Fields(string(data))
return words, nil
}
Follow each return in ReadCorpus to see how an ordinary read failure, a recognizable empty-corpus failure, and success produce different result–error pairs. section 5.3.2











