Issue
I’m trying to create some classes in Swift 5 to represent a directed Graph. I’m finding Swift generics to be very confusing and restrictive.
This is what I’ve got so far, but no matter what I try, Swift seems to come up with some obscure error or other (two are shown below in comments).
Can I even achieve this kind of structure in Swift without hardcoding Node to a specific concrete type?
I want to allow the Node type to be changed, so that I can add additional properties to the Node and Edge types according to the needs of the problem.
public class Graph<N:Node>
{
var nodeMap: [String: N] = [:]
var edges: [Edge<N>] = []
public func addEdge(_ parentName: String, _ childName: String, weight: Double = 0.0) throws {
let parent:N? = nodeMap[parentName]
let child:N? = nodeMap[childName]
let newEdge = Edge(parent!, child!)
parent!.outgoing.append(newEdge) // Cannot convert value of type 'Edge<N>' to expected argument type 'Edge<Node>'
edges.append(newEdge)
}
}
public class Edge<N:Node> {
var parent: N
var child: N
init(_ parent: N, _ child: N) {
self.parent = parent
self.child = child
}
}
public class Node {
var name:String = ""
var outgoing:[Edge<Self.Type>] = [] //'Edge' requires that 'Self.Type' inherit from 'Node'
}
Solution
I think you need to make Node
a protocol:
public protocol Node : class {
var name:String { get set }
var outgoing:[Edge<Self>] { get set }
}
Then, you can create concrete Node
conformers that you can use as the generic argument for graph. e.g.
final public class ConcreteNode: Node {
public var name = ""
public var outgoing: [Edge<ConcreteNode>] = []
}
And you can create a Graph<ConcreteNode>
. If you want to have a node with a foo
property, you can create that class too:
final public class NodeWithFoo: Node {
public var name = ""
public var outgoing: [Edge<NodeWithFoo>] = []
public var foo = ""
}
And you can have another Graph<NodeWithFoo>
.
Answered By – Sweeper
Answer Checked By – Candace Johnson (BugsFixing Volunteer)