如何针对遵循IDable协议的可选数组扩展通用方法?
问题与解决方案
问题场景
首先定义以下结构体:
struct Screw: Codable { let id: String // 其他属性 } struct Nail: Codable { let id: String // 其他属性 } struct Parts: Codable { var screws: [Screw]? var nails: [Nail]? }
为Optional<[Screw]>编写扩展后可以正常调用:
extension Optional { mutating func blah() where Wrapped == [Screw] { if self == nil { self = [] } if self!.count > 13 && self![13].id == "666" { self?.remove(at: 13) } } } // 调用正常 stuff.parts.screws.blah()
但定义IDable协议并让结构体遵循后,尝试编写通用扩展时编译报错:
protocol IDable { var id: Int { get } } struct Screw: Codable, IDable { let id: Int // 其他属性 } struct Nail: Codable, IDable { let id: Int // 其他属性 } // 错误的扩展写法 extension Optional { mutating func blah() where Wrapped == [IDable] { // 逻辑代码 } } // 调用时报错:Instance method 'blah()' requires the types '[Screw]' and '[any IDable]' be equivalent stuff.parts.screws.blah()
可行实现方式
问题根源在于[Screw](具体类型数组)和[any IDable](存在类型数组)并非同一类型,无法通过Wrapped == [IDable]的约束匹配。正确的做法是通过泛型约束,让扩展适用于所有元素遵循IDable的可替换集合:
extension Optional where Wrapped: RangeReplaceableCollection, Wrapped.Element: IDable { mutating func blah() { // 初始化空集合(如果当前是nil) if self == nil { self = Wrapped() } guard let collection = self, collection.count > 13 else { return } // 获取第14个元素(下标13) let targetIndex = collection.index(collection.startIndex, offsetBy: 13) if collection[targetIndex].id == 666 { self?.remove(at: targetIndex) } } }
说明
RangeReplaceableCollection协议提供了init()(创建空集合)和remove(at:)方法,刚好匹配原逻辑的需求;- 通过
Wrapped.Element: IDable约束,确保集合中的元素都拥有id属性; - 这种写法不仅支持
[Screw]和[Nail],还支持其他遵循RangeReplaceableCollection且元素符合IDable的类型。
如果只需要针对数组优化,可以将约束更具体:
extension Optional where Wrapped == Array<Element>, Element: IDable { mutating func blah() { if self == nil { self = [] } guard let count = self?.count, count > 13 else { return } if self![13].id == 666 { self?.remove(at: 13) } } }
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

