errors.New и fmt.Errorf
В прошлом уроке ошибка была накладной, которая приходит вместе с посылкой. Объявлять для каждой такой накладной свой тип с методом Error() — много кода ради одной строки. Для обычного случая в стандартной библиотеке есть две готовые функции. Давайте разберём каждую.
Две функции для ошибок
Обе функции возвращают значение типа error, и его возвращают из функции, как любое значение. Отличаются они тем, каким может быть текст.
errors.New
Функция errors.New из пакета errors создаёт ошибку с заданным текстом. Она принимает ровно одну строку:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("age is negative")
fmt.Println(err) // age is negative
fmt.Println(err == nil) // false
}Давайте разберём пример по строкам:
errors.New("age is negative")создаёт ошибку и кладёт её вerr.- Первый
Printlnпечатает текст ошибки:age is negative. - Второй печатает
false: созданная ошибка не равнаnil.
errors.New похожа на готовый штамп «товар повреждён»: текст одинаковый при каждом оттиске. Её берут, когда текст всегда один и тот же. Ограничение — текст задан раз и навсегда: подставить в него число errors.New не умеет.
Здесь картинка со штампом неточна. Два оттиска одного штампа читаются как одна и та же пометка. А два вызова errors.New("age is negative") дают две разные ошибки: текст у них совпадает, но сравнение == между ними даёт false.
fmt.Errorf
Когда в сообщение нужно вставить значение, пишите fmt.Errorf. Это уже пометка от руки: «коробка 3 из 5 помята». Эта функция форматирует строку по тем же правилам, что и fmt.Sprintf, но возвращает не string, а error. Знаки те же, что у Printf:
%d— для числа;%q— для строки в кавычках;%v— для любого значения, в том числе для другой ошибки.
Функция checkAge пользуется обеими функциями:
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>Давайте разберём checkAge:
- Для отрицательного возраста она возвращает ошибку с постоянным текстом через
errors.New. - Для возраста больше
150она вызываетfmt.Errorf: в тексте нужно само число. ВызовcheckAge(200)печатаетage 200 is too big. - Для допустимого возраста функция возвращает
nil, иfmt.Printlnпечатает<nil>.
Знаки %w и %v
Другую ошибку в fmt.Errorf можно вставить двумя знаками, и результат у них разный.
Знак %w
Знак %w оборачивает уже готовую ошибку: новая ошибка хранит старую внутри, и функция errors.Is позже находит её там. Это как приколоть старую накладную к новой: текст новый, а исходная пометка осталась при нём.
Знак %v
Знак %v вставляет только текст, а саму ошибку не сохраняет. Он переписывает старую пометку своими словами, и исходной ошибки внутри новой уже нет.
Какую функцию выбрать
Функцию выбирают только по тексту ошибки:
- текст всегда одинаковый — хватает
errors.New; - в тексте есть число, имя или другая ошибка — нужна
fmt.Errorf.
Как писать текст
Сообщения ошибок в Go пишут по четырём правилам:
- текст начинается со строчной буквы;
- в конце нет точки;
- сообщение называет, что не получилось и с каким значением:
age 200 is too big, а неbad input; - в тексте нет слов
errorилиfailed: что это ошибка, видно и так, ведь значение пришло в переменнуюerr.
Первые два правила появились потому, что сообщения склеивают. Вызывающий код добавляет перед текстом свой контекст через двоеточие. Из fmt.Errorf("load user: %v", err) получится load user: age is negative, и строка читается как одна фраза. Заглавная буква и точка оказались бы в её середине.
Частая ошибка
Число попытались вставить в текст через errors.New:
err := errors.New("age %d is too big", 200) // ✗too many arguments in call to errors.NewФункция errors.New принимает ровно одну строку и ничего не форматирует: штамп не умеет вписывать числа. Для текста со значениями нужна fmt.Errorf("age %d is too big", n).
Главное
errors.Newсоздаёт ошибку с постоянным текстом.fmt.Errorfвставляет значения в текст по правиламPrintfи возвращаетerror.%wвfmt.Errorfсохраняет исходную ошибку внутри новой, а%v— только её текст.- Текст ошибки начинается со строчной буквы и не заканчивается точкой.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.