Перейти к содержимому
Теория · Ошибки · урок 4 из 11

Проверка if err != nil

Накладную из прошлых уроков читают сразу, пока коробка ещё закрыта. Посмотрим, почему с ошибкой поступают так же. Функция strconv.Atoi превращает строку в int и возвращает пару (int, error). Если строка — не целое число, функция отвергает её целиком, даже когда та начинается с цифр:

GO
package main

import (
    "fmt"
    "strconv"
)

func main() {
    n, err := strconv.Atoi("42")
    fmt.Println(n, err) // 42 <nil>
    n, err = strconv.Atoi("7 days")
    fmt.Println(n, err) // 0 strconv.Atoi: parsing "7 days": invalid syntax
}

Давайте разберём пример по строкам:

  1. strconv.Atoi("42") разбирает строку, и программа печатает 42 <nil>.
  2. Для "7 days" функция возвращает 0 вместе с ошибкой, а не прочитанную цифру 7.
  3. Второй Println печатает 0 и текст ошибки. Текст называет функцию и строку, которую не удалось разобрать.

Но 0 бывает и настоящим результатом: strconv.Atoi("0") тоже даёт 0. Удачу от неудачи отличает только err, а не число. По закрытой коробке не видно, что в ней не то, а по накладной видно.

Проверка сразу после вызова

Поэтому ошибку проверяют в следующей же строке после вызова: if err != nil. Если ошибка есть, функция сразу возвращает её вызывающему коду, а дальше идёт только удачный путь:

GO
func total(a, b string) (int, error) {
    x, err := strconv.Atoi(a)
    if err != nil {
        return 0, err
    }
    y, err := strconv.Atoi(b)
    if err != nil {
        return 0, err
    }
    return x + y, nil
}

Функция total разбирает две строки и складывает числа. Давайте разберём её по шагам:

  1. x, err := strconv.Atoi(a) разбирает первую строку.
  2. Проверка if err != nil при ошибке возвращает 0 и саму ошибку, и остальной код не выполняется.
  3. y, err := strconv.Atoi(b) разбирает вторую строку. Здесь := допустимо, потому что слева есть новая переменная y. Переменная err при этом не создаётся заново, а получает новое значение.
  4. Вторая проверка работает так же, как первая.
  5. return x + y, nil выполняется, только если обе строки разобраны.

Ранний return

Такой выход из функции сразу при ошибке называют ранним return. Посылку с плохой накладной отправляют обратно сразу, не распаковывая. У этого приёма два плюса:

  • каждая проверка короткая и стоит сразу за своим вызовом;
  • основной код остаётся без лишних отступов: сложение стоит в конце, на первом уровне.

Кто решает, что делать

Функция total не печатает ошибку сама, а возвращает её наверх. Решает тот код, который знает, что делать:

  • показать сообщение;
  • попросить ввод заново;
  • завершить программу.

Вызывающий код проверяет ошибку тем же способом:

GO
sum, err := total("12", "x")
if err != nil {
    fmt.Println("total:", err)
    return
}
fmt.Println(sum)

Разберём, что происходит:

  1. total("12", "x") возвращает ошибку: строку "x" разобрать нельзя.
  2. Проверка находит ошибку, и программа печатает total: strconv.Atoi: parsing "x": invalid syntax.
  3. return завершает работу, и до fmt.Println(sum) программа не доходит.

Пока err не равна nil, значение sum не используют: в нём нулевое значение, а не результат.

Здесь картинка с курьером расходится с Go. Курьер не уйдёт, пока вы не распишетесь в накладной. А компилятор Go требует только одного: объявленную переменную err нужно где-то прочитать.

Частая ошибка

Переменную err, которую ни разу не прочитали, компилятор не пропустит: declared and not used: err. Поэтому её иногда заменяют на _ и берут число как есть:

GO
n, _ := strconv.Atoi("ten") // ✗
fmt.Println(n * 2)          // 0

Программа компилируется и печатает 0, хотя строка вообще не число. Накладную выбросили не читая, и неверный ввод тихо превратился в ноль и пошёл дальше в расчёты. Сохраните ошибку в err и проверьте её до того, как использовать n.

Главное

  • Ошибку проверяют в следующей строке после вызова: if err != nil.
  • При ошибке функция сразу возвращает её наверх — это ранний return.
  • Пока err != nil, другие результаты не используют: strconv.Atoi при ошибке возвращает 0.
  • Ошибку не выбрасывают через _: неверный ввод тихо превратится в нулевое значение.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом