В практической ситуации преобразование элементов через map может завершиться ошибкой: каким образом Swift передаёт ошибку из замыкания вызывающему коду?
map объявлен с поведением rethrows: он передаёт ошибку наружу только тогда, когда переданное замыкание действительно может её выбросить. Поэтому вызов map с бросающим замыканием требует try, а при первой ошибке преобразование прекращается и результат не возвращается.
Функциональные методы коллекций должны работать как с обычными замыканиями, так и с замыканиями, способными завершиться ошибкой. Механизм rethrows решает эту задачу без необходимости считать каждый вызов map, filter или reduce потенциально бросающим.
Так API сохраняет точность: обработчик ошибок требуется только там, где ошибка действительно возможна. Это уменьшает лишний шаблонный код и позволяет компилятору проверять обработку ошибок на границе вызова.
Предположим, элементы коллекции нужно преобразовать, но часть входных данных может быть некорректной. Если преобразование выбросит ошибку, важно понимать, вернёт ли map частичный массив, продолжит ли обработку остальных элементов или немедленно завершит вызов.
Неверное предположение о частичном результате может привести к использованию неполных данных. Дополнительно следует учитывать побочные эффекты внутри замыкания: действия, выполненные до ошибки, автоматически не откатываются.
Упрощённо сигнатура map для Sequence выглядит так: замыкание преобразует один элемент в новый тип, а сам метод возвращает массив этих результатов. Если замыкание объявлено как бросающее, map становится бросающим для данного вызова благодаря rethrows.
В примере map успешно преобразует первые два значения, но при встрече с "x" замыкание выбрасывает ошибку. map немедленно прекращает обход, не возвращает частичный [10, 20], а ошибка передаётся вызывающему коду.
try ставится перед выражением, которое может выбросить ошибку, а не перед каждым выполнением замыкания. Если замыкание не бросает ошибку, map можно вызвать без try. Само rethrows не позволяет методу самостоятельно выбросить произвольную ошибку: источник ошибки должен находиться в переданном замыкании.
Для обычного Array преобразование обычно выполняется сразу. Для ленивой последовательности обработка элементов также происходит по запросу, но ошибка всё равно возникает в момент фактического получения соответствующего элемента; это не превращает ошибку в частичный успешный результат.
Сервис получает массив строковых идентификаторов и должен преобразовать их в числа. Возможны два подхода: сначала отфильтровать некорректные строки, а затем преобразовать оставшиеся, либо выполнить бросающее преобразование через map.
Фильтрация удобна, если некорректные значения допустимы и их нужно молча пропустить. Однако она может скрыть повреждение входных данных и изменить количество элементов. Бросающий map сохраняет строгий контракт: либо преобразованы все элементы, либо вызывающий код получает ошибку.
Для финансовых или идентификационных данных выбирают второй вариант. Обнаружение первой ошибки останавливает построение результата, а вызывающий слой может записать причину, отклонить весь запрос или применить отдельную политику восстановления. Если внутри замыкания есть журналирование или счётчики, их состояние нужно проектировать отдельно, поскольку ошибка не выполняет транзакционный откат.
Дополнительный вопрос: возвращает ли map частичный результат, если ошибка возникла после нескольких успешно обработанных элементов?
Нет. У map результатом является единое возвращаемое значение, и при выброшенной ошибке это значение не возвращается. Уже выполненные преобразования могут иметь побочные эффекты, но промежуточный массив недоступен вызывающему коду.
Дополнительный вопрос: может ли rethrows выбросить ошибку, созданную внутри самого map, независимо от замыкания?
Нет. rethrows означает, что метод может передать наружу ошибку из аргумента-замыкания, но не произвольно добавить собственный источник ошибок. Если операция должна выбрасывать ошибку независимо от переданного замыкания, её API должно быть объявлено как обычное throws.
Дополнительный вопрос: что изменится, если бросающее преобразование заменить на Result?
Ошибка перестанет распространяться через механизм throws: она станет обычным значением результата, например элементом типа Result. Это позволяет обработать ошибки отдельных элементов и получить результат для всей коллекции, но усложняет типы и требует явно определить, как сочетать успешные и ошибочные элементы.
Бросающий map подходит для политики «вся операция успешна или завершается ошибкой». Result или специальное накопление ошибок лучше, когда нужно сохранить успешные преобразования, собрать несколько диагностик или продолжить обработку после некорректного элемента.