2

I have a protocol Node:

protocol Node {
   var parent: Node?
   var children: [Node]
}

which is implemented by classes:

class TreeNode: Node {
   var parent: Node?
   var children: [Node]
}

But this poses a problem as accessing the parent in TreeNode now gives me a Node, and I want to do TreeNode specific operations on them. So I want to change the protocol to:

protocol Node {
   associatedtype T: Node

   var parent: T?
   var children: [T]
}

Which let's me define the class as:

class TreeNode: Node {
   var parent: TreeNode?
   var children: [TreeNode]
}

Great! But there's a catch. If I want to write a helper method for Node that deals with arrays:

func getReversedChildren<T: Node>(node: T) -> [T] {
   return node.children.reversed()
}

The compiler fails with the error: Cannot convert return expression of type 'ReversedCollection<[T.T]>' to return type '[T]'

From what I can gather about this issue, I need to achieve a type-erasure mechanism to support this architecture. But how can this be done on my example?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Oskar
  • 3,625
  • 2
  • 29
  • 37

1 Answers1

3

You probably want the parent and children of a node to be of the same type as the node itself, not just some type conforming to Node. That would be Self in the protocol definition:

protocol Node {
    var parent: Self? { get set }
    var children: [Self] { get set }
}

Now you can define the concrete class (see A Swift protocol requirement that can only be satisfied by using a final class for why the class needs to be final):

final class TreeNode: Node {
    var parent: TreeNode? = nil
    var children: [TreeNode] = []
}

and

func getReversedChildren<T: Node>(node: T) -> [T] {
    return node.children.reversed()
}

compiles without problems.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382