Почему структура должна соответствовать Hashable, а также универсальному массиву при преобразовании в словарь

Цель состояла в том, чтобы преобразовать элементы общего списка в словарь для переменной uniqueKeys, но я увидел ошибку:

Не удается подписать значение неправильного или неоднозначного типа

Я знал, что что-то нужно для соответствия протоколу Hashable, и в конце концов нашел решение, но я не совсем понимаю, почему это решение работает.

  1. Я понимаю, почему T должно быть Hashable, поскольку оно входит в ключ словаря, но почему еще и CustomSet?
  2. Если CustomSet использует Hashable, почему мне не нужно ничего писать в его расширении?

исходный код

struct CustomSet<T : Comparable > {
    private var list: [T]
    init(_ list: [T]){
        let uniqueKeys = list.reduce(into: [:]){ dict, item in
            dict[item, default: 0] += 1
        }.keys
        self.list = Array(uniqueKeys)
    }
}

extension CustomSet : Equatable {
    static func == (lhs: CustomSet, rhs: CustomSet) -> Bool {
        return lhs.list.count == rhs.list.count && lhs.list.sorted() == rhs.list.sorted()
    }
}  

Я, наконец, решил это с помощью:

struct CustomSet<T : Comparable > : Hashable where T : Hashable {
    private var list: [T]
    init(_ list: [T]){
        let uniqueKeys = list.reduce(into: [:]){ dict, item in
            dict[item, default: 0] += 1
        }.keys
        self.list = Array(uniqueKeys)
    }
}

extension CustomSet : Equatable {
    static func == (lhs: CustomSet, rhs: CustomSet) -> Bool {
        return lhs.list.count == rhs.list.count && lhs.list.sorted() == rhs.list.sorted()
    }
}  

Я также заметил, что факторизация в расширении: extension CustomSet: Hashable where T : Hashable не похожа на struct CustomSet<T : Comparable > : Hashable where T : Hashable с точки зрения добавления протокола Hashable, потому что я все еще вижу эту ошибку.

Что я пробовал

Если я добавлю это к универсальному типу T, я все равно увижу ту же ошибку.

struct CustomSet<T : Comparable, Hashable >

Если я добавлю Hashable к CustomSet, я увижу ошибку

Не удается преобразовать значение типа «[T]» в ожидаемый тип аргумента «UnsafeRawBufferPointer».

а также

Наследование от непротокольного типа «Hashable»

extension CustomSet : Equatable, Hashable {
    static func == (lhs: CustomSet, rhs: CustomSet) -> Bool {
        return lhs.list.count == rhs.list.count && lhs.list.sorted() == rhs.list.sorted()
    }

    func hash(into hasher: inout Hasher) {
       var hashValue: Int {
           var hasher = Hasher()
           self.hash(into: &hasher)
           return hasher.finalize()
       }
    }
 }

person Turnipdabeets    schedule 08.01.2019    source источник
comment
Пожалуйста, начните с проблемы, а не с решения: Какой код вызывает начальное сообщение об ошибке «Невозможно индексировать значение неправильного или неоднозначного типа», о котором вы говорите?   -  person Martin R    schedule 08.01.2019
comment
CustomSet не обязательно должно быть Hashable, ваш код прекрасно компилируется без соответствия Hashable. Кстати, какой смысл сокращать список до Dictionary, чьи ключи являются элементами списка, если тогда вы просто назначаете ключи указанного словаря list? На самом деле вы не уникальные эти ключи.   -  person Dávid Pásztor    schedule 08.01.2019
comment
@MartinR только что добавил исходный код.   -  person Turnipdabeets    schedule 08.01.2019
comment
@DávidPásztor Словарь возьмет массив типа [1,1] и вернет ключ только 1 один раз - может быть, я смогу переименовать его в removeDuplicates   -  person Turnipdabeets    schedule 08.01.2019
comment
@Turnipdabeets: Тогда я не понимаю вашего вопроса. Как говорит Дэвид, CustomSet не обязательно должен быть Hashable. struct CustomSet<T : Comparable & Hashable > компилирует исходный код.   -  person Martin R    schedule 08.01.2019
comment
Ой! Когда я впервые попробовал struct CustomSet<T : Comparable, Hashable >, мне просто не хватало & в struct CustomSet<T : Comparable & Hashable >.   -  person Turnipdabeets    schedule 08.01.2019
comment
Кстати, ваше равенство будет работать так же, если вы удалите lhs.list.count == rhs.list.count. Если структура неизменна, вы можете выполнить сортировку в инициализаторе.   -  person Sulthan    schedule 08.01.2019
comment
Кстати, с вашей пользовательской реализацией Hashable много чего не так...   -  person Martin R    schedule 08.01.2019
comment
@MartinR Это неполное, но рад услышать, что вы хотите сказать!   -  person Turnipdabeets    schedule 08.01.2019
comment
@Turnipdabeets: для начала обратите внимание, что ваш func hash(into hasher: inout Hasher) ничего не делает (кроме объявления неиспользуемого вычисляемого свойства).   -  person Martin R    schedule 08.01.2019


Ответы (1)


Нет необходимости заставлять CustomSet соответствовать Hashable. Простое добавление ограничения Hashable к параметру универсального типа устраняет ошибку.

Не удается подписать значение неправильного или неоднозначного типа

, что ожидаемо, так как ключи Dictionary должны соответствовать Hashable.

Кстати, синтаксис для объявления соответствия нескольким протоколам — &, а не ,, поэтому в предложении where нет необходимости.

struct CustomSet<T : Comparable & Hashable> {
    private var list: [T]
    init(_ list: [T]){
        let uniqueKeys = list.reduce(into: [:]){ dict, item in
            dict[item, default: 0] += 1
            }.keys
        self.list = Array(uniqueKeys)
    }
}

extension CustomSet : Equatable {
    static func == (lhs: CustomSet, rhs: CustomSet) -> Bool {
        return lhs.list.count == rhs.list.count && lhs.list.sorted() == rhs.list.sorted()
    }
}

Более того, вы можете просто сделать self.list = Array(Set(list)) в инициализаторе, чтобы сохранить только уникальные элементы из ввода в вашем list, нет необходимости возиться с Dictionary.

person Dávid Pásztor    schedule 08.01.2019
comment
Спасибо, Давид, я избегаю использования Set, потому что пишу свой собственный CustomSet :) - person Turnipdabeets; 08.01.2019
comment
@Turnipdabeets a Set просто упрощено Dictionary. Если вы не хотите использовать Set, то использование Dictionary — это жульничество :) - person Sulthan; 08.01.2019