Какой уровень Optional связывает шаблон с постфиксом вопросительного знака при сопоставлении вложенного Optional?
Постфиксный шаблон ? проверяет и снимает ровно один внешний слой Optional. Если значение имеет тип Int??, то после сопоставления case let value? переменная value сохраняет тип Int?, а не превращается сразу в Int.
Optional в Swift моделирует наличие или отсутствие значения через тип, концептуально состоящий из вариантов none и some(Wrapped). Такой подход заставляет явно обрабатывать отсутствие значения вместо неявного использования специального nil.
Pattern matching был добавлен в систему типов как способ проверять структуру значений и одновременно извлекать их содержимое. Для Optional это позволяет различать внешний nil и присутствующее значение, которое само может быть Optional.
Вложенный Optional возникает, когда уже опциональное значение помещается в другой Optional, например при работе с обобщёнными функциями или API, где результат операции сам может быть Optional. В таком случае nil внешнего слоя и nil внутреннего слоя означают разные состояния.
Если разработчик считает, что шаблон ? снимает все уровни, он может ошибочно передать дальше Int? как обычный Int, потерять различие между двумя видами отсутствия или неверно интерпретировать результат сопоставления.
Шаблон с постфиксом ? соответствует только значению вида some(...) внешнего Optional и связывает его содержимое. Для Int?? возможны такие состояния: внешний nil, внешний some с внутренним nil и внешний some с внутренним some(Int).
Поэтому при сопоставлении case let value? с Int??:
nil не совпадает;some(nil) совпадает, а value получает Int? со значением nil;some(some(42)) совпадает, а value получает Int? со значением 42.В примере шаблон снимает внешний слой, поэтому inner имеет тип Int?, а напечатанное значение — true. Чтобы извлечь Int, нужно сопоставить ещё один Optional-слой, например вторым условием или дополнительным шаблоном.
Это отличается от безусловного предположения о типе: pattern matching сначала проверяет структуру значения, поэтому извлечение не приводит к аварийному завершению. Однако каждый снятый слой нужно обрабатывать осознанно, особенно если состояния внешнего и внутреннего nil имеют различный смысл.
Сервис возвращает Int??: внешний nil означает, что ответ ещё не получен, внутренний nil — что сервер ответил, но значение отсутствует, а some(Int) — что значение найдено. При проверке только одного уровня разработчик может принять полученный внутренний nil за отсутствие самого ответа.
Возможны три подхода:
В данном случае выбран pattern matching по уровням, потому что бизнес-логика различает «ответ не получен» и «ответ получен без значения». Результат — отсутствие ошибочной интерпретации данных; цена решения — необходимость явно поддерживать все состояния вложенного типа.
Что произойдёт, если внутренний Optional содержит значение?
Внешний шаблон ? успешно совпадёт и свяжет внутренний Optional целиком. Если исходное значение — some(some(42)), связанная переменная всё ещё имеет тип Int?, но содержит 42; автоматического снятия второго слоя не происходит.
Чем шаблон ? отличается от последовательного if let?
Оба механизма могут извлекать значения, но шаблон ? участвует в общем сопоставлении структуры и явно соответствует одному уровню some. Последовательный if let обычно используют для пошагового извлечения и перехода к следующему уровню, поэтому он лучше показывает намерение, когда каждый уровень обрабатывается отдельно.
Почему уплощение вложенного Optional может быть ошибкой?
Уплощение объединяет разные состояния в одно: внешний nil и внутренний nil начинают выглядеть одинаково. Это допустимо только тогда, когда различие не имеет смысла для предметной области; если оно влияет на повтор запроса, отображение состояния или обработку ошибки, нужно сохранить уровни и сопоставлять их явно.