[SOLVED] Swift generics for graph classes

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)

Leave a Reply

Your email address will not be published. Required fields are marked *