Swift中协议作为类型无法遵守Hashable协议问题求助
错误原因
Protocol 'TestElement' as a type cannot conform to 'Hashable' 错误的核心逻辑是:Swift 中协议作为存在型(existential type)本身默认不遵守任何协议,哪怕你声明了TestElement继承自Hashable,直接将TestElement作为类型传入Set的泛型参数时,Set要求元素类型遵守Hashable,而协议类型本身无法满足这个约束。
解决方案
方案1:使用泛型枚举(推荐,类型安全,无版本限制)
给枚举添加泛型参数,约束泛型遵守TestElement协议,此时Set的元素为具体的遵守协议的类型,天然满足Hashable要求:
enum TestEnum<T: TestElement> { case all case some(_ testElements: Set<T>) }
适用场景:同一个TestEnum实例关联的所有TestElement都是同一种具体类型的场景。
方案2:使用类型擦除(兼容旧版本,支持存储不同类型的TestElement)
如果需要在同一个Set里存储多种不同的TestElement实现类型,可以先实现一个类型擦除的包装结构体:
public struct AnyTestElement: TestElement { public let identifier: String private let equalHandler: (Any) -> Bool private let hashHandler: (inout Hasher) -> Void public init<E: TestElement>(_ element: E) { identifier = element.identifier equalHandler = { other in guard let otherElement = other as? E else { return false } return element == otherElement } hashHandler = { hasher in element.hash(into: &hasher) } } public static func == (lhs: AnyTestElement, rhs: AnyTestElement) -> Bool { lhs.equalHandler(rhs) } public func hash(into hasher: inout Hasher) { hashHandler(&hasher) } }
之后将枚举的关联值类型改为Set<AnyTestElement>即可:
enum TestEnum { case all case some(_ testElements: Set<AnyTestElement>) }
使用时只需要把具体的TestElement实例用AnyTestElement包装后再存入Set。
方案3:使用Swift 5.7+ 存在型语法(最简写法,有版本要求)
Swift 5.7 引入的SE-0309特性解锁了存在型对Hashable/Equatable的默认遵守,只要协议本身的要求没有除Hashable/Equatable之外的Self或关联类型约束,就可以直接用any关键字声明存在型:
enum TestEnum { case all case some(_ testElements: Set<any TestElement>) }
适用场景:项目部署目标为iOS 16+/macOS 13+/watchOS 9+/tvOS 16+ 及更高版本。
内容的提问来源于stack exchange,提问作者Kelton
相关产品推荐
相关产品推荐

