Найкращі практики - обробка помилок

Це перша стаття з серії уроків, які я засвоїв за пару років, з яких я працював у виробництві Go. Ми здійснюємо велику кількість Go-послуг у виробництві на Saltside Technologies (psst, я наймаю на кілька посад в Бангалорі для Saltside), а також я веду власний бізнес, де Go є невід’ємною частиною.

Ми будемо висвітлювати широкий спектр предметів, великих і малих.

Перша тема, яку я хотів висвітлити в цій серії, - це обробка помилок. Це часто викликає плутанину і роздратування у нових розробників Go.

Деякий фон - Інтерфейс помилок

Просто ми на одній сторінці. Як ви можете знати, помилка в Go - це просто все, що реалізує інтерфейс помилок. Ось як виглядає визначення інтерфейсу:

інтерфейс помилки типу {
    Помилка ()
}

Отже, все, що реалізує рядок Error (), може використовуватися як помилка.

Перевірка на наявність помилок

Використання структур помилок та перевірки типу

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

Кращим підходом є використання типів помилок. Таким чином, ви можете (звичайно) створювати структури, які реалізують інтерфейс помилок, а потім робити порівняння типу в операторі комутатора.

Ось приклад реалізації помилки.

введіть структуру ErrZeroDivision {
    рядок повідомлення
}
func NewErrZeroDivision (рядок повідомлення) * ErrZeroDivision {
    return & ErrZeroDivision {
        повідомлення: повідомлення,
    }
}
func (e * ErrZeroDivision) Error () рядок {
    повернути е.міс
}

Тепер цю помилку можна використовувати так.

func main () {
    результат, помилка: = ділити (1,0, 0,0)
    якщо помилка! = нуль {
        помилка перемикача (тип) {
        випадок * ErrZeroDivision:
            fmt.Println (err.Error ())
        за замовчуванням:
            fmt.Println ("Що за h * щойно сталося?")
        }
    }
    fmt.Println (результат)
}
func поділ (a, b float64) (float64, помилка) {
    якщо b == 0,0 {
        повернути 0,0, NewErrZeroDivision ("Неможливо розділити на нуль")
    }
    повернути а / б, нуль
}

Ось посилання Go Play для повного прикладу. Зверніть увагу на помилку (тип) схеми перемикача, яка дає можливість перевірити різні типи помилок, а не щось інше (наприклад, порівняння рядків чи щось подібне).

Використання пакету помилок та прямого порівняння

Вищеописаний підхід можна альтернативно використовувати, використовуючи пакет помилок. Цей підхід рекомендується використовувати для перевірки помилок у пакеті, де потрібно швидке представлення помилок.

var errNotFound = errors.New ("Елемент не знайдено")
func main () {
    err: = getItem (123) // Це би кинуло errNotFound
    якщо помилка! = нуль {
        помилка перемикача {
        case errNotFound:
            log.Println ("Запитаний елемент не знайдено")
        за замовчуванням:
            log.Println ("Невідома помилка")
        }
    }
}

Цей підхід менш хороший, коли вам потрібні складніші об'єкти помилок, наприклад коди помилок тощо. У цьому випадку вам слід створити власний тип, який реалізує інтерфейс помилок.

Негайне поводження з помилками

Іноді я стикаюся з кодом, як показано нижче (але, як правило, з більшою пухкістю навколо)

помилка func example1 () {
    помилка: = call1 ()
    помилка повернення
}

Сенс у тому, що помилка не вдається вирішити негайно. Це неміцний підхід, оскільки хтось може вставити код між err: = call1 () та return Err, що порушило б намір, оскільки це може затьмарити першу помилку. Два альтернативних підходи:

// Згорнути повернення та помилку.
помилка func example2 () {
    повернути call1 ()
}
// Робіть явну обробку помилок відразу після дзвінка.
помилка func example3 () {
    помилка: = call1 ()
    якщо помилка! = нуль {
        помилка повернення
    }
    повернути нуль
}

Обидва вищевказані підходи зі мною добре. Вони досягають того самого, що є; якщо комусь потрібно щось додати після call1 (), він повинен подбати про обробку помилок.

Це все на сьогодні

Слідкуйте за наступною статтею про найкращі практики Go. Ідіть сильно :).

func main () {
    err: = readArticle ("Перейти найкращі практики - обробка помилок")
    якщо помилка! = нуль {
        ping ("@ sebdah")
    }
}