Go Best Practices - Обработка ошибок

Это первая статья из серии уроков, которые я выучил за пару лет, когда работал в Go на производстве. Мы производим большое количество сервисов Go в производстве в Saltside Technologies (psst, я нанимаю несколько рабочих мест в Бангалоре для Saltside), и я также управляю собственным бизнесом, где Go является неотъемлемой частью.

Мы рассмотрим широкий круг вопросов, больших и маленьких.

Первой темой, которую я хотел осветить в этой серии, является обработка ошибок. Это часто вызывает замешательство и раздражение у новых разработчиков Go.

Некоторый фон - интерфейс ошибки

Просто мы на одной странице. Как вы, возможно, знаете, ошибка в Go - это просто все, что реализует интерфейс ошибок. Вот как выглядит определение интерфейса:

интерфейс типа ошибки {
    Ошибка () строка
}

Поэтому все, что реализует строковый метод Error (), может использоваться как ошибка.

Проверка на ошибки

Использование структур ошибок и проверка типов

Когда я начал писать Go, я часто проводил сравнения строк сообщений об ошибках, чтобы увидеть, какой был тип ошибки (да, стыдно думать, но иногда вам нужно оглядываться назад, чтобы идти вперед).

Лучшим подходом является использование типов ошибок. Таким образом, вы можете (конечно) создать структуры, которые реализуют интерфейс ошибок, а затем выполнить сравнение типов в операторе switch.

Вот пример реализации ошибки.

Тип ErrZeroDivision struct {
    строка сообщения
}
func NewErrZeroDivision (строка сообщения) * ErrZeroDivision {
    return & ErrZeroDivision {
        сообщение: сообщение,
    }
}
func (e * ErrZeroDivision) Error () string {
    возврат электронного сообщения
}

Теперь эту ошибку можно использовать вот так.

func main () {
    результат, ошибка: = разделить (1,0, 0,0)
    если ошибаться! = ноль {
        ошибка переключения (тип) {
        case * ErrZeroDivision:
            fmt.Println (err.Error ())
        по умолчанию:
            fmt.Println («Что за х * только что произошло?»)
        }
    }
    fmt.Println (результат)
}
func Делить (a, b float64) (float64, ошибка) {
    если b == 0.0 {
        вернуть 0.0, NewErrZeroDivision («Невозможно разделить на ноль»)
    }
    возврат а / б, ноль
}

Вот ссылка Go Play для полного примера. Обратите внимание на шаблон ошибки err. (Type), который позволяет проверять различные типы ошибок, а не что-то еще (например, сравнение строк или что-то подобное).

Использование пакета ошибок и прямое сравнение

Вышеупомянутый подход может альтернативно быть обработан, используя пакет ошибок. Этот подход рекомендуется для проверки ошибок в пакете, где вам нужно быстрое представление ошибок.

var errNotFound = errors.New («Элемент не найден»)
func main () {
    err: = getItem (123) // Это вызовет errNotFound
    если ошибаться! = ноль {
        switch err {
        case errNotFound:
            log.Println («Запрошенный элемент не найден»)
        по умолчанию:
            log.Println («Произошла неизвестная ошибка»)
        }
    }
}

Этот подход менее хорош, когда вам нужны более сложные объекты ошибок, например, коды ошибок и т. д. В этом случае вы должны создать свой собственный тип, который реализует интерфейс ошибки.

Немедленная обработка ошибок

Иногда я сталкиваюсь с кодом, подобным приведенному ниже (но обычно с большим количеством ошибок):

func example1 () error {
    ошибка: = call1 ()
    вернуть ошибку
}

Дело в том, что ошибка не обрабатывается сразу. Это хрупкий подход, поскольку кто-то может вставить код между err: = call1 () и return err, что нарушит намерение, поскольку это может скрыть первую ошибку. Два альтернативных подхода:

// Свернуть возврат и ошибку.
func example2 () error {
    обратный вызов1 ()
}
// Делаем явную обработку ошибок сразу после вызова.
func example3 () error {
    ошибка: = call1 ()
    если ошибаться! = ноль {
        вернуть ошибку
    }
    вернуть ноль
}

Оба вышеперечисленных подхода подходят мне. Они достигают того же самого, что есть; если кому-то нужно добавить что-то после call1 (), ему нужно позаботиться об обработке ошибок.

Это все на сегодня

Следите за следующей статьей о Go Best Practices. Иди сильным :).

func main () {
    err: = readArticle ("Go Best Practices - Обработка ошибок")
    если ошибаться! = ноль {
        пинг ( "@ sebdah")
    }
}