Swift 4泛型学习遇编译错误,请求原因分析
帮你排查Swift 4泛型协议的编译问题
嘿,我来帮你拆解这段代码里可能导致编译失败的点,还有优化方向:
首先先把你贴的代码整理成格式化的样子,方便分析:
protocol NodeType { associatedtype T associatedtype E var parent: T {get set} var children: [T] {get set} var item: E {get set} func getItem()->E mutating func setItem(_ newItem: E) func getParent()->T mutating func setParent(_ theParent:T) func getChild(at index:Int)->T mutating func insertChild(at index:Int, child: T) func find(_ node:T)->Int } protocol Item... // 这里你没写完Item协议的内容
最可能导致编译失败的几个原因
关联类型
T缺少关键约束
你的NodeType里,parent和children都是T类型,但编译器完全不知道T是什么——它应该是遵循NodeType的类型吧?必须给T加上约束,不然编译器无法推断类型,直接报错:associatedtype T: NodeType // 约束T必须遵循NodeType本身另外,
find方法需要查找node在children里的索引,这要求T能被比较,所以还要加上Equatable约束:associatedtype T: NodeType & Equatable未完成的
Item协议与E的约束
你写了protocol Item...却没完成内容,如果E是要遵循Item协议的类型,得给E也加上约束,同时补全Item协议的定义,比如:protocol Item { // 比如添加一些Item需要的方法/属性 func itemDescription() -> String } // 在NodeType里给E加约束 associatedtype E: Itemmutating方法的使用场景
如果你的NodeType是给值类型(比如struct)实现的,mutating是必须的,因为值类型修改属性需要显式标记;但如果是给类(class)实现,mutating可以去掉,因为类是引用类型,修改属性不需要这个关键字。如果要让协议同时支持值类型和引用类型,保留mutating就好,类实现的时候会自动忽略这个关键字。根节点的初始化矛盾
你定义的parent是非可选的T类型,但根节点是没有父节点的,这会导致你无法创建第一个节点(总不能把自己当父节点吧?)。所以建议把parent改成可选类型:var parent: T? { get set }
修正后的完整示例代码
// 补全Item协议 protocol Item { func itemDescription() -> String } // 修正后的NodeType协议 protocol NodeType { associatedtype T: NodeType & Equatable associatedtype E: Item var parent: T? { get set } // 改成可选类型,解决根节点初始化问题 var children: [T] { get set } var item: E { get set } func getItem() -> E mutating func setItem(_ newItem: E) func getParent() -> T? // 返回值也要对应改成可选 mutating func setParent(_ theParent: T?) func getChild(at index: Int) -> T? // 加可选避免越界崩溃 mutating func insertChild(at index: Int, child: T) func find(_ node: T) -> Int? // 用可选返回更安全,找不到返回nil而不是-1 } // 实现NodeType的struct示例 struct ConcreteNode<E: Item>: NodeType { // 明确T就是ConcreteNode<E>本身 typealias T = ConcreteNode<E> var parent: ConcreteNode<E>? var children: [ConcreteNode<E>] = [] var item: E func getItem() -> E { return item } mutating func setItem(_ newItem: E) { item = newItem } func getParent() -> ConcreteNode<E>? { return parent } mutating func setParent(_ theParent: ConcreteNode<E>?) { parent = theParent } func getChild(at index: Int) -> ConcreteNode<E>? { guard index >= 0, index < children.count else { return nil } return children[index] } mutating func insertChild(at index: Int, child: ConcreteNode<E>) { guard index >= 0, index <= children.count else { return } children.insert(child, at: index) } func find(_ node: ConcreteNode<E>) -> Int? { return children.firstIndex(of: node) } } // 实现Item协议的示例类型 struct TextItem: Item { var text: String func itemDescription() -> String { return "Text Item: \(text)" } } // 使用示例 let rootItem = TextItem(text: "Root Node") var rootNode = ConcreteNode(parent: nil, item: rootItem) let childItem = TextItem(text: "Child Node") var childNode = ConcreteNode(parent: rootNode, item: childItem) rootNode.insertChild(at: 0, child: childNode) print(rootNode.find(childNode)) // 输出 Optional(0)
额外提示
- 如果你的
NodeType只打算给类实现,可以把mutating关键字去掉,这样代码更简洁。 find方法返回Int?比返回-1更符合Swift的安全编程理念,调用者可以通过可选绑定处理找不到的情况。
内容的提问来源于stack exchange,提问作者Easiwriter
相关产品推荐
相关产品推荐

