Проверка if err != nil
Накладную из прошлых уроков читают сразу, пока коробка ещё закрыта. Посмотрим, почему с ошибкой поступают так же. Функция strconv.Atoi превращает строку в int и возвращает пару (int, error). Если строка — не целое число, функция отвергает её целиком, даже когда та начинается с цифр:
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
}Давайте разберём пример по строкам:
strconv.Atoi("42")разбирает строку, и программа печатает42 <nil>.- Для
"7 days"функция возвращает0вместе с ошибкой, а не прочитанную цифру7. - Второй
Printlnпечатает0и текст ошибки. Текст называет функцию и строку, которую не удалось разобрать.
Но 0 бывает и настоящим результатом: strconv.Atoi("0") тоже даёт 0. Удачу от неудачи отличает только err, а не число. По закрытой коробке не видно, что в ней не то, а по накладной видно.
Проверка сразу после вызова
Поэтому ошибку проверяют в следующей же строке после вызова: if err != nil. Если ошибка есть, функция сразу возвращает её вызывающему коду, а дальше идёт только удачный путь:
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 разбирает две строки и складывает числа. Давайте разберём её по шагам:
x, err := strconv.Atoi(a)разбирает первую строку.- Проверка
if err != nilпри ошибке возвращает0и саму ошибку, и остальной код не выполняется. y, err := strconv.Atoi(b)разбирает вторую строку. Здесь:=допустимо, потому что слева есть новая переменнаяy. Переменнаяerrпри этом не создаётся заново, а получает новое значение.- Вторая проверка работает так же, как первая.
return x + y, nilвыполняется, только если обе строки разобраны.
Ранний return
Такой выход из функции сразу при ошибке называют ранним return. Посылку с плохой накладной отправляют обратно сразу, не распаковывая. У этого приёма два плюса:
- каждая проверка короткая и стоит сразу за своим вызовом;
- основной код остаётся без лишних отступов: сложение стоит в конце, на первом уровне.
Кто решает, что делать
Функция total не печатает ошибку сама, а возвращает её наверх. Решает тот код, который знает, что делать:
- показать сообщение;
- попросить ввод заново;
- завершить программу.
Вызывающий код проверяет ошибку тем же способом:
sum, err := total("12", "x")
if err != nil {
fmt.Println("total:", err)
return
}
fmt.Println(sum)Разберём, что происходит:
total("12", "x")возвращает ошибку: строку"x"разобрать нельзя.- Проверка находит ошибку, и программа печатает
total: strconv.Atoi: parsing "x": invalid syntax. returnзавершает работу, и доfmt.Println(sum)программа не доходит.
Пока err не равна nil, значение sum не используют: в нём нулевое значение, а не результат.
Здесь картинка с курьером расходится с Go. Курьер не уйдёт, пока вы не распишетесь в накладной. А компилятор Go требует только одного: объявленную переменную err нужно где-то прочитать.
Частая ошибка
Переменную err, которую ни разу не прочитали, компилятор не пропустит: declared and not used: err. Поэтому её иногда заменяют на _ и берут число как есть:
n, _ := strconv.Atoi("ten") // ✗
fmt.Println(n * 2) // 0Программа компилируется и печатает 0, хотя строка вообще не число. Накладную выбросили не читая, и неверный ввод тихо превратился в ноль и пошёл дальше в расчёты. Сохраните ошибку в err и проверьте её до того, как использовать n.
Главное
- Ошибку проверяют в следующей строке после вызова:
if err != nil. - При ошибке функция сразу возвращает её наверх — это ранний
return. - Пока
err != nil, другие результаты не используют:strconv.Atoiпри ошибке возвращает0. - Ошибку не выбрасывают через
_: неверный ввод тихо превратится в нулевое значение.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.