Программирование SwiftОбработка ошибокРазработчик приложений на Swift

Можно ли в Swift повторно выбросить перехваченную ошибку без явного указания значения?

Можно ли в Swift повторно выбросить перехваченную ошибку без явного указания значения?

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

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

Нет. В Swift нет отдельного оператора для безымянного повторного выброса ошибки из catch: нужно явно выбросить связанную переменную, обычно через throw error. Это сохраняет исходное значение ошибки, включая её динамический тип и associated values.

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

Модель ошибок Swift построена вокруг явного управления потоком: объявление throws показывает, что вызов может завершиться ошибкой, а throw явно указывает, какое значение передаётся вверх. Такой подход отделяет намеренное повторное выбрасывание ошибки от создания новой ошибки с изменённым смыслом.

Явность особенно важна при добавлении контекста. Разработчик должен сознательно выбрать: передать исходную ошибку без изменений или заменить её доменной ошибкой, сохранив исходную причину отдельно.

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

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

Молчаливое создание новой ошибки вместо исходной также опасно: вызывающий код может потерять точную причину сбоя, associated values и возможность сопоставить ошибку с конкретным случаем перечисления.

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

В catch ошибка доступна через локальную переменную error, если она не была явно переименована. Для повторной передачи нужно написать throw error либо выбросить явно связанную переменную из шаблона catch.

enum ReadError: Error { case unavailable } func read() throws -> Int { throw ReadError.unavailable } func load() throws -> Int { do { return try read() } catch { print("Ошибка зафиксирована") throw error } }

Вызов load() получит то же значение ошибки, которое выбросила read(). В данном примере это ReadError.unavailable, а не новая обезличенная ошибка.

rethrows не является оператором повторного выброса. Это модификатор объявления функции, который ограничивает возможность ошибки ошибками, возникающими из переданных ей бросающих замыканий. Для повторной передачи конкретной ошибки внутри catch используется обычный throw с выражением.

Если требуется изменить уровень абстракции, следует создать новую доменную ошибку и сохранить исходную причину отдельным свойством или associated value. Это даёт вызывающему коду стабильный контракт, но требует заранее определить, какую диагностическую информацию API обязан сохранить.

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

Сервис загрузки перехватывает низкоуровневую ошибку хранилища. Вариант с throw error прост и сохраняет полную информацию, но раскрывает детали инфраструктуры вызывающему коду. Вариант с новой ошибкой вроде serviceUnavailable формирует более стабильный публичный контракт, однако без сохранения исходной причины ухудшает диагностику.

Практичное решение — выбросить доменную ошибку, содержащую исходную ошибку как причину, если это соответствует архитектуре API. Если слой не меняет абстракцию и лишь выполняет журналирование, следует использовать throw error; результатом будет сохранение исходного типа, данных и возможности точного сопоставления в вызывающем коде.

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

  1. Что произойдёт, если в catch объявить собственное имя ошибки?

    Неявное имя error больше не следует использовать как источник истины: повторно выбрасывать нужно явно связанную переменную с выбранным именем. Это снижает риск случайно передать не ту ошибку после преобразования или дополнительной обработки.

  2. Сохраняется ли при throw error конкретный тип ошибки, если функция объявлена просто как throws?

    Да. throws не стирает динамический тип выброшенного значения и не ограничивает его одним заранее объявленным типом. Если исходная ошибка является конкретным случаем перечисления, вызывающий код сможет сопоставить его в catch, пока между слоями не была создана новая ошибка.

  3. Чем отличается повторный throw error от создания новой ошибки с тем же текстом?

    throw error сохраняет исходное значение и его структурированные данные. Новая ошибка с тем же текстом может быть логически похожей, но это уже другой тип или экземпляр; проверки по случаю перечисления, associated values и исходной причине могут перестать работать. Текст сообщения не должен быть единственным контрактом обработки ошибок.