Метод содержит набор возвращает другое значение в разное время - PullRequest
0 голосов
/ 02 декабря 2018

Я думал о том, как Swift обеспечивает уникальность для Set, потому что я бесплатно переключил один из моих объектов с Equatable на Hashable, и поэтому я придумал эту простую игровую площадку

struct SimpleStruct: Hashable {
    let string: String
    let number: Int

    static func == (lhs: SimpleStruct, rhs: SimpleStruct) -> Bool {
        let areEqual = lhs.string == rhs.string
        print(lhs, rhs, areEqual)
        return areEqual
    }
}

var set = Set<SimpleStruct>()
let first = SimpleStruct(string: "a", number: 2)
set.insert(first)

Мой первый вопрос был:

Будет ли вызываться метод static func == каждый раз, когда я вставляю новый объект в набор?

Мой вопрос возникает из этой мысли:

Для Equatable obj, чтобы принять это решение, единственный способ убедиться, что два объекта obj одинаковы, - запросить результат static func ==.

Для Hashable obj более быстрый способ - сравнить hashValue s ... но, как и в моем случае, реализация по умолчанию будет использовать string и number, в отличие от логики ==.

Итак, чтобы проверить, как Set ведет себя, я только что добавил оператор печати. ​​

Я понял, что иногда я получал оператор печати, иногда нет.Как иногда hashValue недостаточно для принятия этого решения ... Так что метод вызывается не каждый раз.Странно ...

Итак, я попытался добавить два равных объекта и задавался вопросом, что будет результатом set.contains

let second = SimpleStruct(string: "a", number: 3)
print(first == second) // returns true
set.contains(second)

И чудес чудес, запуская парураз на игровой площадке я получал разные результаты, и это могло привести к непредсказуемым результатам ... Добавление

var hashValue: Int {
    return string.hashValue
}

избавляет от любых неожиданных результатов, но я сомневаюсь:

Почему безпользовательская реализация hashValue, иногда вызывается ==, а иногда нет?Должна ли Apple избегать такого неожиданного поведения?

Returns false returns true

1 Ответ

0 голосов
/ 02 декабря 2018

Синтезированная реализация требования Hashable использует все сохраненные свойства struct, в вашем случае string и number.Ваша реализация == основана только на строке:

let first = SimpleStruct(string: "a", number: 2)
let second = SimpleStruct(string: "a", number: 3)

print(first == second) // true
print(first.hashValue == second.hashValue) // false

Это нарушение требования протокола Hashable:

Два равных экземпляра должны передавать одинаковые значения Hasher в хэш (в :) в том же порядке.

и вызывать неопределенное поведение.(А поскольку значения хеш-функции рандомизированы со времени Swift 4.2, поведение может быть разным при каждом запуске программы.)

Что вероятно происходит в вашем тесте, так это то, что значение хеш-функции second равноиспользуется для определения «корзины» набора, в котором будет храниться значение.Это может быть или не быть тем же самым контейнером, в котором хранится first.- Но это деталь реализации: неопределенное поведение - неопределенное поведение, оно может привести к неожиданным результатам или даже к ошибкам во время выполнения.

Реализация

var hashValue: Int {
    return string.hashValue
}

или альтернативно (начиная с Swift 4.2)

func hash(into hasher: inout Hasher) {
    hasher.combine(string)
}

исправляет нарушение правила и, следовательно, заставляет ваш код работать так, как ожидалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...