使用存在类型作为关联值而非类型擦除时无法遵循协议的原因
包含存在类型的枚举无法自动合成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
相关产品推荐
相关产品推荐

