Swift枚举符合可识别:类型不符合可识别协议

Swift enum conformance to identifiable: Type doesn't conform to Identifiable protocol

我有一个具有关联值的枚举,我想将其用作 RxDataSources 中的一个项目。我尝试通过如下所示将其符合 Hashable 使其符合可识别

enum DriverHubWidget: Hashable, Identifiable {
    static func == (lhs: DriverHubWidget, rhs: DriverHubWidget) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
    
    var id: Int { hashValue }
    
    case greetings(DriverHubGreetingsViewModel)
    case scorecard(DriverHubScorecardSummary?, Error?)
    case optOut
    
    func hash(into hasher: inout Hasher) {
        switch self {
        case .greetings( _):
            return hasher.combine(1)
        case .scorecard( _, _):
            return hasher.combine(2)
        case .optOut:
            return hasher.combine(3)
        }
    }
}

我通过简单地为每个案例分配一个 Int 值来实现哈希函数。然后为了符合可识别性,我加了一个id属性即returns的hashValue。这编译得很好。

现在,当我尝试使用它来声明截面模型的类型别名时,如下所示

typealias WidgetSection = AnimatableSectionModel<String, DriverHubWidget>

它编译并抛出错误,Type 'DriverHubWidget' does not conform to protocol 'IdentifiableType'

我不明白为什么它不起作用,当枚举符合 Hashable 和 Identifiable 时它编译得很好,但是当使用时一致性不知何故无效 是因为枚举的关联值不是 Hashable 吗?

您混淆了 Identifiable, a Swift built-in protocol, with IdentifiableType,RxDataSource 库中的一个协议。

你可以只符合IdentifiableType

enum DriverHubWidget: Hashable, IdentifiableType {
    
    var identity: Int {
        hashValue
    }

    ...
}

虽然我觉得你遵守 Hashable 的方式很奇怪。您认为枚举的两个值只要大小写相同就相等,而忽略它们的关联值。也就是说,.greeting(x) == .greeting(y) 为真。这似乎相当违反直觉。如果这确实是您 identity 想要的,您可能只想以这种方式实现 identity

var identity: Int {
    switch self {
    case .greetings( _):
        return 1
    case .scorecard( _, _):
        return 2
    case .optOut:
        return 3
    }
}

并通过实际考虑相关值来符合 Hashable,或者根本不符合 Hashable

(这不是一个完整的答案,但对于评论来说太长了。将其视为 Sweeper 已经说过的内容的附录)

要成为 Identifiable 的对象,它们需要具有稳定的(即不随时间变化)身份概念,以将它们与其他相关对象区分开来。究竟哪种身份概念对您的目的有意义取决于您。正如 the documentation 提到的:

Identifiable leaves the duration and scope of the identity unspecified.

  • Identities can have any of the following characteristics: Guaranteed always unique, like UUIDs.
  • Persistently unique per environment, like database record keys.
  • Unique for the lifetime of a process, like global incrementing integers.
  • Unique for the lifetime of an object, like object identifiers.
  • Unique within the current collection, like collection indices. It’s up to both the conformer and the receiver of the protocol to document the nature of the identity.
  1. 您可能不想忽略身份概念中的相关值。否则,您的代码可能会尝试将两个对象视为相同,即使它们的关联值不同。

    实际上,这意味着 DriverHubGreetingsViewModelDriverHubScorecardSummary 也需要符合 Identifiable。您的 Error? 关联值,您可能想要制作成 (Error & Identifiable)?*

  2. 您不能将 id 的实现精细化为 hashValue,因为哈希值(按设计)是不可预测的。完全有可能你的所有三个案例都以相同的 id 结束(如果散列以 1、2 和 3 的散列全部冲突的方式播种,就会发生这种情况)

另一个注意事项:Swift 中有一个可选模型,后面跟着一个可选错误是一种代码味道。这是 Objective C 的遗留问题,其类型系统缺乏一种轻量级的方式来表达一种类型 另一种类型(“或”或“求和”类型)的值。 Swift 支持具有关联值的枚举(您已经在使用一个!),甚至可以是通用的。标准库中已经内置了一个:Result<Success, Failure>

因此,相比 case scorecard(DriverHubScorecardSummary?, Error?),我建议:

case scorecard(Result<DriverHubScorecardSummary, Error & Identifiable>)