Предскажите результат и объясните, как is учитывает Optional в этом коде:
let value: Int? = 42
print(value is Int)
print(value is Int?)
Код напечатает:
Проверка value is Int успешно извлекает непустое значение из Optional, а value is Int? проверяет совместимость с самим типом Optional. Статический тип выражения остаётся Int?; оператор is выполняет проверку совместимости типов во время выполнения.
Optional появился как типобезопасный способ представить отсутствие значения без неявного null и связанных с ним аварийных ошибок. Поэтому значение Int? хранит либо Int, либо nil, а компилятор заставляет явно учитывать возможность отсутствия значения.
При этом Swift поддерживает работу с динамическими типами через Any, is, as? и pattern matching. Эти механизмы должны корректно обрабатывать не только сами значения, но и их Optional-обёртки.
Статический тип value — Int?, поэтому легко ошибочно предположить, что проверка value is Int обязана вернуть false. На практике оператор проверяет не простое совпадение написанных в коде типов, а возможность привести фактическое значение к целевому типу.
Неверное понимание этого поведения приводит к путанице между статическим типом выражения, фактическим наличием значения и результатом динамического приведения. Особенно опасно считать, что успешная проверка is уже даёт доступ к значению как к не-Optional.
В value находится Optional.some(42). При проверке value is Int Swift видит непустой Optional и проверяет его содержимое: 42 совместимо с Int, поэтому результат — true.
Проверка value is Int? также возвращает true, поскольку исходное значение уже имеет тип Int?. Для непустого Optional обе проверки успешны, но они проверяют разные целевые типы: содержимое и обёртку соответственно.
Оператор is ничего не извлекает в переменную и не изменяет тип value. Он возвращает только Bool. Для безопасного использования содержимого требуется отдельное извлечение:
Здесь if let делает number типом Int, а is Int уже избыточен: после успешного извлечения статический тип number и так равен Int. Поэтому в обычном коде для Optional следует применять optional binding, а не использовать is как замену извлечению.
Для nil результат проверки содержимого был бы иным: nil is Int возвращает false, потому что извлечь значение типа Int невозможно. Проверка совместимости с Optional может быть успешной, поскольку само значение имеет Optional-тип, но конкретное поведение следует рассматривать с учётом целевого уровня обёртки.
Представим обработчик данных из внешнего источника, где значение имеет тип Any и может быть числом или отсутствовать. Вариант с проверкой is позволяет узнать, совместимо ли значение с типом, но не даёт самого числа:
Проблема такого решения — неочевидная работа с Optional-слоем и отсутствие извлечённого значения. Принудительное приведение через as! Int короче, но при неожиданном типе завершит приложение аварийно.
Условительное приведение as? Int безопаснее, однако его результат снова будет Optional и потребует обработки nil. Для внешних данных предпочтителен вариант с условительным приведением и явной веткой ошибки: он не скрывает отсутствие значения и не приводит к аварийному завершению.
Вопрос: Превращает ли успешная проверка value is Int переменную value из Int? в Int?
Ответ: Нет. is возвращает только Bool и не меняет статический тип выражения. Чтобы получить Int, нужен if let, guard let или другой способ извлечения Optional.
Вопрос: Почему для let value: Int? = nil проверка value is Int не может быть успешной?
Ответ: У Optional нет содержащего значения, которое можно было бы проверить или привести к Int. Поэтому проверка содержимого завершается false. Сам факт наличия типа Int? не означает наличие объекта типа Int внутри него.
Вопрос: Чем is Int отличается от as? Int для Optional-значения?
Ответ: is Int возвращает только признак совместимости типов — Bool. as? Int выполняет условительное приведение и возвращает извлечённое значение типа Int?: при успехе — число, при неуспехе — nil. Поэтому as? применяют, когда после проверки нужно использовать результат, а is — когда достаточно самого факта совместимости.