Тип error: возврат, проверка и оборачивание
Содержание курса
Проверка ошибки через if err != nil и функция validatePositive
После вызова функции, возвращающей error, принято проверять результат немедленно и завершать ветку с ошибкой явно — через return, return err или передачу выше:
result, err := someFunc()
if err != nil {
return err // или log + return, или return 0, err
}
// только здесь result можно использовать согласно контракту функции
Ключ — в завершении ветки. Если после if err != nil { ... } нет return или другого выхода, выполнение продолжится с тем же result. Что именно содержит result при ошибке — зависит от контракта конкретной функции: одни возвращают нулевое значение, другие — частичный результат (например, io.Reader.Read сообщает, сколько байт успело прочитаться до сбоя). Считать result пригодным без явного разрешения контракта нельзя.
Соберём полный пример:
package main
import (
"errors"
"fmt"
)
var ErrNotPositive = errors.New("число должно быть положительным")
func validatePositive(n int) (int, error) {
if n <= 0 {
return 0, ErrNotPositive
}
return n, nil
}
func process(n int) (int, error) {
result, err := validatePositive(n)
if err != nil {
return 0, fmt.Errorf("process: %w", err)
}
return result, nil
}
func main() {
var n int
fmt.Scan(&n)
result, err := process(n)
if err != nil {
fmt.Println("ошибка:", err)
if errors.Is(err, ErrNotPositive) {
fmt.Println("причина: число не положительное")
}
return
}
fmt.Println("результат:", result)
}
Запустим с вводом -3:
ошибка: process: число должно быть положительным причина: число не положительное
Несколько деталей, на которые стоит обратить внимание.
validatePositive и process возвращают 0 в позиции int при ошибке — не потому что 0 что-то означает, а просто как неиспользуемое значение: такой контракт выбран явно. Именно поэтому process, увидев err != nil от validatePositive, немедленно возвращает 0, fmt.Errorf(...) — значение результата отбрасывается, ошибка с контекстом поднимается выше.
errors.Is внутри main находит исходный ErrNotPositive даже сквозь обёртку "process: " — этот механизм разобран в предыдущей секции, здесь мы видим его в действии целиком.
Теперь о том, что происходит при пропуске проверки:
result, _ := process(-3) // ошибка выброшена
fmt.Println(result) // печатает 0 — но это нулевое значение по контракту process при сбое, не результат вычисления
Компилятор не запретит такой код. result равен 0 не потому что вычисление дало ноль, а потому что произошёл сбой — и process намеренно вернула 0 по своему контракту. Никакого предупреждения нет — ошибка растворилась. Поэтому _ для ошибки допустим только когда вы точно знаете, что сбой в данном месте невозможен или заведомо несущественен.
