В
Swift 6.4 добавили
Hashable для нескольких типов стандартной библиотеки. Раньше их можно было сравнивать, но нельзя было использовать как ключи словаря или элементы в
Set. Изменения небольшие, но убирают лишние преобразования и упрощают несколько паттернов.
Dictionary.Keys стал Hashable:
Свойство
keys у словаря возвращает представление
Dictionary.Keys. Раньше его можно было перебирать и сравнивать, но не использовать в
Set или как ключ словаря.
Теперь это работает напрямую:
var knownSchemas: Set<[String: Any].Keys> = []
knownSchemas.insert(payload.keys)
Тип значения словаря не обязан быть
Hashable. Ключи словаря уже имеют это требование, поэтому
[String: Any].Keys получает соответствие.
Семантика равенства:
Два
Dictionary.Keys равны, когда содержат одинаковые ключи. Значения словаря и порядок обхода не влияют на результат.
let first = ["id": 1, "name": 10]
let second = ["name": 20, "id": 2]
print(first.keys == second.keys) // true
Оба словаря содержат ключи
id и
name, поэтому их представления ключей равны.
Хеширование следует той же логике. Перестановка ключей не меняет хеш. Это работает, потому что порядок обхода словаря не является частью его идентичности.
Когда порядок важен, нужно использовать упорядоченную коллекцию:
let orderedKeys = dictionary.keys.sorted()
Когда это полезно:
Новое соответствие пригодится, когда набор полей важнее, чем значения под ними.
Например приложение может отслеживать схемы динамических данных без создания отдельного
Set для каждого словаря:
struct PayloadSchemaRegistry {
private var schemas: Set.Keys> = []
mutating func register(_ payload: [String: Value]) {
schemas.insert(payload.keys)
}
func contains(_ payload: [String: Value]) -> Bool {
schemas.contains(payload.keys)
}
}
Возможные сценарии: удаление повторяющихся структур данных, кеширование результатов по доступным полям, группировка метаданных по набору ключей, передача представлений ключей в обобщенные
API, требующие
Hashable.
Почему Dictionary.Values не стал Hashable:
Значения словаря могут дублироваться. Игнорирование порядка с сохранением количества дубликатов требует семантики мультимножества. В
Swift нет стандартной абстракции мультимножества, а эффективное равенство потребовало бы дополнительного хранилища.
Поэтому соответствие добавлено только для типов с четкими правилами равенства и хеширования.
CollectionOfOne и EmptyCollection:
CollectionOfOne - коллекция ровно с одним элементом. Теперь она условно
Hashable, когда ее элемент
Hashable. Это редко встречается в прикладном коде, но важно для обобщенных алгоритмов.
EmptyCollection тоже стал
Hashable. Все экземпляры одного типа
EmptyCollection равны, потому что не содержат элементов. Это улучшает согласованность стандартной библиотеки.
UnownedTaskExecutor стал Hashable:
UnownedTaskExecutor - это невладеющая ссылка на исполнителя задач
Swift Concurrency. Раньше он поддерживал равенство, но не хеширование.
Без
Hashable для связи исполнителей с ресурсами приходилось использовать список и линейный поиск:
let connection = entries.first {
$0.executor == executor
}?.connection
Теперь можно использовать исполнитель как ключ словаря:
var connections: [UnownedTaskExecutor: DBConnection] = [:]
connections[executor] = connection
Это дает поиск в среднем за
O(1) вместо
O(n).
Важно - время жизни исполнителя:
UnownedTaskExecutor не удерживает исполнителя. Хранение его как ключа словаря не продлевает жизнь исполнителя.
Владелец словаря должен удалять записи, когда связанный исполнитель завершает работу. Иначе словарь будет содержать ключи, ссылающиеся на исполнители, которых больше нет.
Новое соответствие улучшает производительность поиска, но не меняет правила владения.
Вывод:
Dictionary.Keys:
Hashable - самое полезное изменение. Оно позволяет представлениям ключей напрямую участвовать в
Set, словарях и обобщенных
API без промежуточных преобразований.
CollectionOfOne и
EmptyCollection закрывают недостающие части обобщенной модели стандартной библиотеки.
UnownedTaskExecutor:
Hashable решает более узкую задачу, но дает инфраструктуре конкурентности эффективный способ связывать ресурсы с идентификаторами исполнителей.
Главное правило не изменилось: если вы храните невладеющую ссылку на исполнителе, вы должны сами следить за тем, чтобы она не осталась висеть после того, как исполнитель завершил работу.