Вопрос с собеседования · Go

Как в Go обрабатывают ошибки и зачем нужны errors.Is, errors.As и %w?

JuniorОшибки и panic
Ещё спрашивают так:
  • Почему в Go нет исключений?
  • Как обернуть ошибку в Go, сохранив исходную?
Коротко

Ошибка — обычное значение типа error, которое функция возвращает последним: if err != nil { return ... }. Оборачивают с контекстом через fmt.Errorf("...: %w", err). Проверяют errors.Is (совпадение с конкретной ошибкой в цепочке) и errors.As (найти ошибку нужного типа).

  • Сравнение err == ErrNotFound не работает для обёрнутых — только errors.Is
  • Сигнальные ошибки: var ErrNotFound = errors.New("not found")
  • Свой тип ошибки — для данных (код, поле)

А ещё errors.Join (1.20) — объединить несколько ошибок

Подробно

Почему значения, а не исключения. Путь ошибки виден в коде явно: каждая функция, которая может упасть, это показывает, а вызывающий решает, что делать. Нет скрытых переходов через несколько уровней стека. Цена — многословность.

Оборачивание. %w сохраняет исходную ошибку внутри новой; цепочка раскручивается errors.Unwrap. Сообщение получается вида «repo.Get 42: dial tcp: connection refused» — контекст каждого уровня. %v вместо %w добавит текст, но разорвёт цепочку для Is и As.

Правила:

  • обрабатывать ошибку один раз: либо вернуть с контекстом, либо залогировать, но не оба;
  • не игнорировать (_ = f()) без явной причины;
  • не сравнивать по тексту err.Error();
  • сигнальные ошибки — часть API пакета, менять их нельзя.

Пример

var ErrNotFound = errors.New("not found")

func (r *Repo) Get(id int) (*User, error) {
    u, err := r.db.query(id)
    if errors.Is(err, sql.ErrNoRows) {
        return nil, ErrNotFound
    }
    if err != nil {
        return nil, fmt.Errorf("repo.Get %d: %w", id, err)
    }
    return u, nil
}

if errors.Is(err, ErrNotFound) { http.Error(w, "нет", 404) }

var ve *ValidationError
if errors.As(err, &ve) { log.Println(ve.Field) }

Что хотят услышать

  • Ошибки как значения.
  • Оборачивание через %w.
  • errors.Is и errors.As.
  • Правила обработки.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Ошибки в Go обрабатывают исключениями try/catch
  • ✗err == ErrNotFound работает и для ошибок, обёрнутых через fmt.Errorf с %w
  • ✗%v сохраняет цепочку для errors.Is так же, как %w
  • ✓Ошибка — значение; оборачивают через %w, проверяют errors.Is/As

Что спросят дальше

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования