За счёт какого механизма Optional<Int> может быть ключом словаря, а Optional<T> для произвольного T — нет?
Это обеспечивается условным соответствием протоколам: Optional получает соответствие Hashable только тогда, когда его тип Wrapped сам соответствует Hashable. Поэтому Optional<Int> допустим как ключ словаря, а Optional<T> без ограничения T: Hashable — нет.
Система типов Swift должна была сохранить безопасность обобщённого кода без предположения, что любое значение можно корректно сравнивать и хешировать. Условные соответствия позволяют описывать свойства контейнера через свойства содержащегося типа, не добавляя некорректные соответствия для всех возможных T.
Словарь использует ключ не только для хранения значения, но и для его поиска. Для этого ключ должен иметь согласованные операции равенства и хеширования: равные ключи обязаны иметь одинаковый хеш.
Если бы любой Optional автоматически считался Hashable, то Optional значения, которые нельзя надёжно сравнить или хешировать, могли бы использоваться как ключи. Это нарушило бы требования словаря на этапе компиляции.
Optional — это обобщённый тип с двумя состояниями: отсутствующее значение и значение Wrapped. Стандартная библиотека условно предоставляет ему соответствия Equatable и Hashable, когда Wrapped поддерживает соответствующий протокол.
При хешировании Optional учитывается не только хеш Wrapped, но и состояние Optional. Поэтому nil и some(значение) не рассматриваются как одно и то же значение. Конкретный числовой результат хеширования не является API-контрактом; важно лишь соблюдение правил Hashable.
Ограничение проверяется по статическому типу. Если значение фактически содержит хешируемый объект, но объявлено как Any, этого недостаточно: Any сам по себе не гарантирует Hashable.
Здесь ключ имеет тип Optional<Int>. Int соответствует Hashable, поэтому и Optional<Int> допустим как ключ. Отдельная запись для nil возможна, потому что nil — одно из значений этого ключевого типа.
Сервис кэширует результаты по идентификатору, который иногда отсутствует. Вариант с ключом Optional<Int> технически позволяет отличить отсутствие идентификатора от конкретного идентификатора и хранить специальную запись для nil.
Однако такой дизайн может скрывать ошибку: все объекты без идентификатора попадут под один ключ nil. Безопаснее обычно отфильтровать элементы без идентификатора или явно заменить отсутствие на отдельный доменный ключ. Использование Optional-ключа оправдано, только если единая запись для состояния nil действительно имеет смысл.
Нет. Соответствие определяется статическим типом. Значение, объявленное как Any, нельзя использовать как ключ словаря только потому, что внутри сейчас находится Int: тип Any не предоставляет гарантии Hashable.
Нет. При вложенном Optional это разные уровни состояния. Например, Optional<Int?>.none означает отсутствие внешнего значения, а Optional.some(Optional<Int>.none) — внешнее значение присутствует, но внутри находится nil. Система типов различает эти состояния, поэтому они могут иметь разное равенство и хеширование.
Нельзя допускать изменения значимой для Hashable части ключа, пока ключ используется в словаре. Иначе поиск может вычислить новый хеш и не найти ранее добавленную запись. Для ключей-значений Swift это обычно контролируется неизменяемостью и value semantics, но ссылочные свойства внутри ключа требуют особой осторожности.