Программирование RustОбработка ошибокRust-разработчик серверных приложений

Какой метод Result позволяет выполнить побочный эффект над Err, сохранив исходный результат без преобразова...

Какой метод Result позволяет выполнить побочный эффект над Err, сохранив исходный результат без преобразования?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Для этого используется Result::inspect_err. Он получает ссылку на ошибку, выполняет переданное действие и возвращает исходный Result без изменения его успешного значения или ошибки.

Исторический контекст

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

inspect_err отделяет наблюдение от преобразования. Благодаря этому диагностический побочный эффект не меняет семантику цепочки обработки.

Постановка проблемы

Если логирование встроить вручную между операциями, код становится многословным и легко случайно изменить ошибку или успешное значение. Если использовать map_err, появляется ложный смысл: будто ошибка действительно преобразуется.

Важно также помнить, что inspect_err не обрабатывает ошибку и не устраняет её. После вызова метода Result по-прежнему содержит тот же Ok или Err, поэтому последующие операции, включая оператор ?, работают как раньше.

Подробное решение

inspect_err принимает замыкание, которому передаётся ссылка на значение ошибки. Для Ok замыкание не вызывается, а результат возвращается без изменений. Для Err замыкание выполняется, после чего возвращается исходный Err.

use std::io; fn load() -> Result<String, io::Error> { Err(io::Error::new(io::ErrorKind::NotFound, "файл не найден")) } fn run() -> Result<String, io::Error> { load() .inspect_err(|error| eprintln!("ошибка загрузки: {error}")) }

Замыкание получает &E, поэтому оно не забирает ошибку из Result. Это позволяет безопасно осмотреть её, но не заменить, обернуть или вернуть другую ошибку.

Для преобразования ошибки следует использовать map_err, для восстановления после ошибки — or_else, а для окончательного извлечения значения — методы вроде unwrap_or_else. inspect_err предназначен именно для наблюдения и побочных действий.

Побочный эффект должен быть аккуратным: логирование не должно само вызывать panic, блокировать критический путь или раскрывать чувствительные данные. В многопоточном и высоконагруженном коде также нужно учитывать стоимость форматирования и записи журнала.

Ситуация из практики

Сервис читает настройки и возвращает ошибку вызывающему коду. Команде нужно добавить предупреждение в журнал, но сохранить исходный тип std::io::Error, чтобы верхний уровень мог различать отсутствие файла, отказ в доступе и другие причины.

Вариант с map_err формально решает задачу, но требует вернуть ту же ошибку вручную или создать новую обёртку. Это ухудшает выразительность и повышает риск потерять исходный контекст.

Вариант с отдельным match прозрачен, но дублирует передачу Ok и Err и разрастается при длинной цепочке операций. Выбранный вариант — inspect_err: он сохраняет ошибку и добавляет логирование в одной точке.

В результате код верхнего уровня получает первоначальный Result, может применить ? или выполнить собственную классификацию ошибки, а диагностическая запись создаётся только при неуспехе.

Что кандидаты часто упускают

  1. Вызывает ли inspect_err замыкание для Ok?

Нет. Замыкание вызывается только для варианта Err. Для успешного результата метод является прозрачным: он возвращает Ok с тем же значением и не выполняет диагностическое действие.

  1. Можно ли через inspect_err заменить ошибку или изменить её тип?

Нет. Замыкание получает ссылку на ошибку и не управляет возвращаемым значением метода. Для преобразования содержимого или типа ошибки используется map_err, а для выбора другой ветви обработки — or_else.

  1. Влияет ли inspect_err на последующий оператор ??

Нет, если само замыкание не вызывает аварийное завершение или другой внешний побочный эффект. inspect_err возвращает тот же Result, поэтому оператор ? увидит исходный Ok или Err и применит обычную логику раннего возврата.