В Swift 5 и новее какой тип имеет результат try? для бросающей функции, возвращающей Int?, и почему?
Результат имеет тип Int?, а не Int??. Начиная со Swift 5, try? не добавляет дополнительный уровень optional, если вызываемое выражение уже возвращает optional.
Ранее try? оборачивал результат бросающего выражения в новый optional. Поэтому функция с результатом Int? могла приводить к типу Int??, где один уровень означал ошибку, а другой — успешное отсутствие значения.
В Swift 5 это поведение изменили, чтобы уменьшить неожиданную вложенность optional и сделать try? удобнее для типичных случаев. Такое упрощение улучшило читаемость, но устранило возможность различать ошибку и успешный результат nil только по типу optional.
У функции могут быть два разных исхода: она успешно завершилась и вернула nil либо завершилась ошибкой. После применения try? оба исхода представлены одним значением nil, поэтому причина отсутствия результата теряется.
Это допустимо, если ошибка несущественна и нужно лишь получить значение при успехе. Если ошибка влияет на логику, журналирование или пользовательское сообщение, try? становится слишком грубым механизмом.
try? преобразует ошибку в nil, а успешный результат оставляет доступным. Для функции, возвращающей Int?, Swift 5 и новее сохраняет тип Int?: успешный Int представлен как значение, успешный nil и ошибка — как nil.
Если нужно различать три состояния — значение, успешное отсутствие значения и ошибку, — следует использовать Result<Int?, Error> или явный do-catch. В Result успешный nil находится в success, а ошибка — в failure, поэтому информация сохраняется.
Важное ограничение: try? не предназначен для анализа причины ошибки. Он удобен на необязательных этапах, например при попытке прочитать необязательную настройку, но не на границе доменной операции, где разные сбои требуют разных решений.
Сервис читает значение из локального кэша. Отсутствие записи означает обычный cache miss, а ошибка хранилища означает повреждение данных или недоступность диска.
Вариант с try? краток и позволяет продолжить работу при любом отсутствии значения, но смешивает cache miss с отказом хранилища. Вариант с do-catch сохраняет причины ошибки, однако требует локальной обработки и может связывать инфраструктурный слой с логикой вызывающего кода.
Оптимальным решением на границе слоя будет Result<Значение?, Ошибка>. Он различает успешный nil и failure, позволяет передать ошибку выше без немедленного перехвата и оставляет вызывающему коду выбор стратегии восстановления. Если ошибка действительно неотличима от отсутствия значения, тогда try? будет более простым и оправданным выбором.
try? всегда скрывает только ошибку?Нет. Он также не позволяет отличить ошибку от успешного результата nil, если исходная функция возвращает optional. Поэтому после try? значение nil само по себе не сообщает, какой именно сценарий произошёл.
nil и ошибкой без ручного do-catch?Нужно использовать Result с optional-типом успеха: Result<Int?, Error>. Тогда success(nil) означает корректное отсутствие значения, а failure(error) — фактический сбой. Это особенно полезно при передаче результата между слоями приложения.
try? не означает, что optional больше нельзя вкладывать?Вложенные optional по-прежнему могут существовать в обычных типах Swift. Изменился только способ формирования результата оператором try?: он не создаёт лишний уровень для уже optional-результата. Если модель действительно требует различать несколько уровней отсутствия значения, это нужно выразить отдельным типом или явной моделью состояния, а не полагаться на try?.