Как обрабатывать ошибки при конвертации строки в число
Что проверяют
Знаете ли вы стандартную идиому ошибок в Go и пакет strconv глубже, чем один вызов Atoi. Следом часто спрашивают, как отличить мусор во входной строке от переполнения и что вернуть клиенту API.
Как отвечать
Базовая идиома: функция возвращает значение и error, ошибку проверяют сразу.
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0, fmt.Errorf("parse limit %q: %w", s, err)
}Что стоит рассказать дальше:
AtoiиParseInt:Atoi(s)этоParseInt(s, 10, 0)с приведением кint, а размерintзависит от платформы. Для идентификаторов и сумм явнееParseInt(s, 10, 64), для беззнаковых естьParseUint, для дробныхParseFloat;- тип ошибки: функции возвращают
*strconv.NumErrorс полямиFunc,NumиErr. Причину проверяют черезerrors.Is(err, strconv.ErrSyntax)(не число) илиerrors.Is(err, strconv.ErrRange)(не влезает в тип), сами поля достают черезerrors.As; - переполнение:
ParseIntвозвращает не ноль, а граничное значение типа вместе сErrRange. Использовать результат без проверки ошибки опасно; - строгость: пробелы не прощаются,
" 42"дастErrSyntax, поэтому ввод пользователя сначала пропускают черезstrings.TrimSpace. Знак+допустим, подчёркивания вроде1_000принимаются только приbase = 0; - обёртка:
%wвfmt.Errorfдобавляет контекст и сохраняет цепочку дляerrors.Is.
В HTTP-обработчике такая ошибка означает кривой ввод, значит ответ 400 Bad Request с понятным текстом, а не 500.
Где спотыкаются
Пишут n, _ := strconv.Atoi(s). На мусоре получается ноль, и дальше код молча работает с выдуманным значением.
Сравнивают ошибки по тексту через err.Error(). Текст не часть контракта, для этого есть errors.Is и errors.As.
Забывают, что ParseFloat принимает "NaN" и "Inf". Если число потом идёт в расчёт суммы или в базу, такие значения надо отсекать отдельно.