You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用存在类型作为关联值而非类型擦除时无法遵循协议的原因

包含存在类型的枚举无法自动合成Equatable/Hashable的原因及修复

问题复现

以下代码中,SomeEnum声明遵循Hashable和Equatable,但编译器报错“类型‘SomeEnum’未遵循协议‘Equatable’(及Hashable)”,尽管SomeProtocol本身继承了Hashable和Equatable:

import Foundation

enum SomeEnum: Hashable, Equatable {
    case foo
    case associatedFoo(any SomeProtocol)
}

protocol SomeProtocol: Hashable, Equatable {
    var field: String { get }
}

struct SomeDataType: Hashable, Equatable {
    let field: String
    let anotherField: Int
}

extension SomeDataType: SomeProtocol {}

原因解析

Swift为枚举自动合成Equatable/Hashable实现的核心前提是:所有关联值的类型必须是能确定统一比较/哈希逻辑的具体类型。

而any SomeProtocol是存在类型,它仅表示“某个遵循SomeProtocol的类型实例”,但存在类型本身并没有统一的Equatable实现:

  • 两个any SomeProtocol实例可能属于完全不同的具体类型(比如一个是SomeDataType,另一个是另一个遵循SomeProtocol的结构体),Swift无法自动判断如何对不同类型的实例进行相等性比较。
  • 即使两个实例是同一种具体类型,存在类型本身也不会暴露具体类型的比较逻辑,编译器无法自动推导合成逻辑。

无需类型擦除的修复方案

手动实现Equatable和Hashable,通过判断具体类型来执行比较和哈希操作:

import Foundation

enum SomeEnum: Hashable, Equatable {
    case foo
    case associatedFoo(any SomeProtocol)
    
    static func == (lhs: Self, rhs: Self) -> Bool {
        switch (lhs, rhs) {
        case (.foo, .foo):
            return true
        case let (.associatedFoo(lhsInst), .associatedFoo(rhsInst)):
            // 先校验类型是否一致,再强转为具体类型比较
            guard type(of: lhsInst) == type(of: rhsInst),
                  let lhsConcrete = lhsInst as? some SomeProtocol,
                  let rhsConcrete = rhsInst as? some SomeProtocol else {
                return false
            }
            return lhsConcrete == rhsConcrete
        default:
            return false
        }
    }
    
    func hash(into hasher: inout Hasher) {
        switch self {
        case .foo:
            hasher.combine("foo")
        case let .associatedFoo(inst):
            // 同时哈希类型和实例本身的哈希值,避免不同类型实例哈希冲突
            hasher.combine(type(of: inst))
            inst.hash(into: &hasher)
        }
    }
}

protocol SomeProtocol: Hashable, Equatable {
    var field: String { get }
}

struct SomeDataType: Hashable, Equatable {
    let field: String
    let anotherField: Int
}

extension SomeDataType: SomeProtocol {}

实现说明

  • 相等性判断:只有当两个关联值的具体类型完全一致时,才强转为具体类型执行协议定义的相等性比较,否则直接返回false。
  • 哈希计算:同时将实例的类型和实例本身的哈希值传入Hasher,确保不同类型的实例即使字段值相同,也会有不同的哈希结果,符合Hashable的语义要求。

内容的提问来源于stack exchange,提问作者pmf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:45:01