- Оригинальный адрес:Питер, Северный, если на.org/blog/2019/0…
- Оригинальный автор:Peter
- Адрес перевода:GitHub.com/вода Мел О/…
- Переводчик: хаки хаки
- Уровень переводчика ограничен, если есть ошибка в переводе или понимании, помогите указать
В Go 1.13 представлен расширенныйpackage errors, что примерно нормализует обработку ошибок. Лично я нахожу его API немного запутанным. В этой статье приведены некоторые ссылки на то, как использовать его более эффективно.
создавать ошибки
Ошибки Sentinel такие же, как и раньше. Назовите их ErrXxx и используйте errors.New для их создания.
var ErrFoo = errors.New("foo error")
Типы ошибок в основном такие же, как и раньше. Назовите их XxxError и убедитесь, что у них есть методы Error, соответствующие интерфейсу ошибок.
type BarError struct {
Reason string
}
func (e BarError) Error() string {
return fmt.Sprintf("bar error: %s", e.Reason)
}
Если ваш тип ошибки оборачивает другую ошибку, вам необходимо предоставить метод Unwrap.
type BazError struct {
Reason string
Inner error
}
func (e BazError) Error() string {
if e.Inner != nil {
return fmt.Sprintf("baz error: %s: %v", e.Reason, e.Inner)
}
return fmt.Sprintf("baz error: %s", e.Reason)
}
func (e BazError) Unwrap() error {
return e.Inner
}
упаковка и возврат ошибок
По умолчанию, когда вы сталкиваетесь с ошибкой в функции и вам нужно вернуть ее вызывающей стороне, вы можете передатьfmt.Errorfиз%w
format, оборачивая ошибки соответствующим контекстом.
func process(j Job) error {
result, err := preprocess(j)
if err != nil {
return fmt.Errorf("error preprocessing job: %w", err)
}
Этот процесс называется аннотацией ошибки. Необходимо избегать возврата неаннотированной ошибки, так как это может привести к тому, что вызывающая сторона не узнает, что пошло не так.
Кроме того, рассмотрите возможность оборачивания ошибок пользовательскими типами ошибок (например, BazError выше) для более сложных вариантов использования.
p := getPriority()
widget, err := manufacture(p, result)
if err != nil {
return ManufacturingError{Priority: p, Error: err}
}
проверка ошибок
В большинстве случаев, когда вы получаете сообщение об ошибке, вам не нужно заботиться о деталях. Если выполнение вашего кода не удается, вам необходимо сообщить об ошибке (например, зарегистрировать ее) и продолжить; или, если это невозможно, вы можете использовать контекст, чтобы аннотировать ошибку и вернуть ее вызывающей стороне.
Если вы хотите узнать, какую ошибку вы получаете, вы можете использоватьerrors.IsПроверьте дозорные ошибки, вы также можете использоватьerrors.Asдля проверки значений ошибок.
err := f()
if errors.Is(err, ErrFoo) {
// you know you got an ErrFoo
// respond appropriately
}
var bar BarError
if errors.As(err, &bar) {
// you know you got a BarError
// bar's fields are populated
// respond appropriately
}
errors.Is и errors.As попытаются рекурсивно распаковать ошибки, чтобы найти совпадение.этот кодДемонстрирует базовые методы упаковки и проверки ошибок.(Примечание переводчика: нужно зайти в интернет по-научному, и вставить этот код в конец статьи). Проверятьfunc a()
в порядке проверки, затем попробуйте изменитьfunc c()
Ошибка вернулась, чтобы получить информацию о запущенном процессе.
в видеДокументацияКак уже говорилось, лучше использовать ошибки. Проверить обычные уравнения, например.if err == ErrFoo
; Предпочитаю ошибки. Что касается утверждения общих типов, например.if e,ok := err.(MyError)
, потому что обычная версия не выполняет операцию развертывания. Если вы явно не хотите, чтобы вызывающая сторона разворачивала ошибку, вы можете сделатьfmt.Errorf
Предоставляет различные глаголы форматирования, такие как%v
; или не указывайте неправильный типUnwrap
метод. Но эти случаи не часты.
Пример
package main
import (
"errors"
"fmt"
"log"
)
func main() {
i, err := a()
log.Printf("i=%d err=%v", i, err)
}
//
//
//
func a() (int, error) {
i, err := b()
if errors.Is(err, ErrFoo) {
return 0, fmt.Errorf("tragedy: %w", err)
}
var bar BarError
if errors.As(err, &bar) {
return 0, fmt.Errorf("comedy: %w", err)
}
var baz BazError
if errors.As(err, &baz) {
return 0, fmt.Errorf("farce: %w", err)
}
return i, nil
}
func b() (int, error) {
if err := c(); err != nil {
return 0, fmt.Errorf("error executing c: %w", err)
}
return 1, nil
}
func c() error {
// return ErrFoo
// return BarError{Reason: "😫"}
// return BazError{Reason: "☹️"}
return BazError{Reason: "😟", Inner: ErrFoo}
}
//
//
//
var ErrFoo = errors.New("foo error")
//
//
//
type BarError struct {
Reason string
}
func (e BarError) Error() string {
return fmt.Sprintf("bar error: %s", e.Reason)
}
//
//
//
type BazError struct {
Reason string
Inner error
}
func (e BazError) Unwrap() error {
fmt.Println("fuck")
return e.Inner
}
func (e BazError) Error() string {
if e.Inner != nil {
return fmt.Sprintf("baz error: %s: %v", e.Reason, e.Inner)
}
return fmt.Sprintf("baz error: %s", e.Reason)
}