Представьте, что компилятор отклоняет Result, в котором в качестве ошибки указан собственный тип без специального протокола. Какое требование нарушено?
Параметр Failure в Result<Success, Failure> обязан соответствовать протоколу Error. Поэтому собственный тип ошибки должен явно объявить соответствие Error; иначе такой Result не пройдет проверку типов.
Это ограничение гарантирует, что значение в ветке failure является именно Swift-ошибкой и может использоваться в стандартных механизмах обработки ошибок.
До появления стандартного Result основной моделью передачи ошибок в Swift был механизм throws: функция либо возвращала значение, либо прерывала выполнение исключением. Такой подход удобен для немедленной обработки, но тип ошибки не указывается в сигнатуре.
Result был добавлен в стандартную библиотеку Swift как типизированная модель результата. Его параметр Failure: Error делает контракт ошибки частью типа, сохраняя при этом единый протокол для разных доменных ошибок.
Если разрешить любой тип в позиции ошибки, в failure можно было бы передать строку, число или произвольную структуру без семантики ошибки. Такой API становится неоднородным: вызывающему коду приходится знать случайные соглашения конкретного разработчика.
Соответствие Error предотвращает это на этапе компиляции. Ошибка может быть перечислением, структурой или классом, но она должна явно обозначать свою роль как ошибки.
Неверное моделирование приводит к двум типичным последствиям: код не компилируется либо разработчик заменяет тип на слишком общий Error, теряя точность предметной модели.
Result имеет два параметра: тип успешного значения и тип ошибки. Ограничение применяется только к Failure: успешный тип не обязан соответствовать Error, а тип ошибки обязан.
Здесь StorageError удовлетворяет требованию Failure: Error, а варианты перечисления позволяют безопасно различать причины сбоя без анализа текста сообщения.
Соответствие Error не требует конкретных свойств или методов. Это маркерный протокол, поэтому дополнительные данные ошибки моделируются самим типом: associated values у перечисления, свойства структуры или класса.
Практически предпочтительно указывать конкретный тип ошибки, например Result<String, StorageError>, когда набор причин известен. Использование Result<String, Error> допустимо, но оно стирает статическую информацию о конкретных вариантах и часто вынуждает применять приведения типов или общий обработчик.
Если ошибка проходит через несколько слоев, тип можно преобразовать явно. В Result для этого применяют mapError: успешное значение остается без изменений, а ошибка переводится в тип, понятный следующему слою.
Сервис загрузки данных возвращает результат репозиторию, а репозиторий должен различать отсутствие сети, некорректный ответ и отсутствие авторизации. Вариант с текстовыми сообщениями прост, но ломок: текст нельзя надежно использовать как контракт, его изменение нарушает обработчики.
Вариант с Result<Value, Error> гибче для объединения разных источников, но статически скрывает конкретные причины. Вариант с отдельным перечислением ошибки требует немного больше проектирования, зато дает проверяемый набор состояний и позволяет компилятору контролировать тип результата.
Выбирают доменное перечисление, соответствующее Error, например NetworkError, а низкоуровневые ошибки преобразуют в него через mapError. В результате UI или вызывающий сервис получает стабильные варианты для повторной попытки, показа сообщения или запроса авторизации, не завися от текста системной ошибки.
Да. Ограничение требует только соответствия Error; конкретная форма типа не фиксируется. Перечисление удобно для конечного набора причин, структура — для ошибки с набором данных, класс может быть уместен при необходимости ссылочной семантики или наследования.
Error вместо конкретного типа ошибки?Result<Value, Error> остается корректным, потому что Error сам удовлетворяет ограничению. Однако вызывающий код видит только протокол и теряет информацию о конкретных вариантах на уровне статической типизации; для специальной обработки могут понадобиться приведения или проверка динамического типа.
Нужно определить ошибку целевого слоя и явно преобразовать исходную ошибку, например с помощью mapError. Простое стирание до Error сохраняет возможность передать ошибку, но ухудшает контракт: следующий слой уже не обязан знать полный набор ожидаемых причин и может потерять удобную предметную обработку.