errors.New and fmt.Errorf
In the previous lesson an error was a delivery note that arrives with the parcel. Declaring a separate type with an Error() method for every such note is a lot of code for one line. For the usual case, the standard library has two ready-made functions. Let's go through each.
Two functions for errors
Both functions return a value of type error, and it is returned from a function like any value. They differ in what the text can be.
errors.New
The errors.New function from the errors package creates an error with the given text. It takes exactly one string:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("age is negative")
fmt.Println(err) // age is negative
fmt.Println(err == nil) // false
}Let's walk through the example line by line:
errors.New("age is negative")creates an error and stores it inerr.- The first
Printlnprints the error's text:age is negative. - The second prints
false: the new error is not equal tonil.
errors.New is like a ready-made "item damaged" stamp: the text is the same with every imprint. You use it when the text is always the same. Its limit is that the text is fixed once and for all: errors.New cannot insert a number into it.
Here the stamp picture is inaccurate. Two imprints of one stamp read as the same remark. But two calls to errors.New("age is negative") give two different errors: their texts match, yet comparing them with == gives false.
fmt.Errorf
When the message needs a value in it, write fmt.Errorf. This is a handwritten remark: "box 3 of 5 is dented". It formats the string by the same rules as fmt.Sprintf, but returns an error instead of a string. The verbs are the same as in Printf:
%d—for a number;%q—for a quoted string;%v—for any value, another error included.
The checkAge function uses both functions:
func checkAge(n int) error {
if n < 0 {
return errors.New("age is negative")
}
if n > 150 {
return fmt.Errorf("age %d is too big", n)
}
return nil
}fmt.Println(checkAge(200)) // age 200 is too big
fmt.Println(checkAge(30)) // <nil>Let's go through checkAge:
- For a negative age it returns an error with a fixed text through
errors.New. - For an age above
150the text needs the number itself, so here it isfmt.Errorf, andcheckAge(200)printsage 200 is too big. - For a valid age the function returns
nil, andfmt.Printlnprints<nil>.
The %w and %v verbs
Another error can go into fmt.Errorf through two verbs, and they give different results.
The %w verb
The %w verb wraps an existing error: the new error keeps the old one inside, and the errors.Is function finds it there later. It is like pinning the old note to a new one: the text is new, and the original remark stays attached.
The %v verb
The %v verb inserts only the text and does not keep the error itself. It retells the old remark in its own words, and the original error is no longer inside the new one.
Which function to choose
The choice between the two functions depends only on the text:
- the text is always the same—
errors.Newis enough; - the text contains a number, a name or another error—
fmt.Errorfis needed.
How to write the text
Error messages in Go follow a few rules:
- the text starts with a lowercase letter;
- there is no period at the end;
- the message names what failed and with which value:
age 200 is too big, notbad input; - the text does not say
errororfailed: that it is an error is already clear, since the value came in the variableerr.
The first two rules exist because messages get joined. The calling code adds its own context before the text, separated by a colon. fmt.Errorf("load user: %v", err) gives load user: age is negative, and the line reads as one phrase. A capital letter and a period would end up in its middle.
Common mistake
A number was put into the text through errors.New:
err := errors.New("age %d is too big", 200) // ✗too many arguments in call to errors.NewThe errors.New function takes exactly one string and formats nothing: a stamp cannot fill in numbers. Text with values needs fmt.Errorf("age %d is too big", n).
Key points
errors.Newcreates an error with a fixed text.fmt.Errorfinserts values into the text by thePrintfrules and returns anerror.%winfmt.Errorfkeeps the original error inside the new one, while%vkeeps only its text.- An error message starts with a lowercase letter and does not end with a period.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.