Search code examples
swiftinheritanceself

Stored variable of Self type (especially when subclassing)


class LinkedNode<T> {
    var value: T
    var next: Self?
    
    required init(_ value: T) {
        self.value = value
    }
}

class DoublyNode<T>: LinkedNode<T> {
    weak var previous: DoublyNode?
    override var next: Self? {
        didSet { next.previous = self }
    }
}

I wish it compiles like this. But it doesn't.

Stored property cannot have covariant 'Self' type

I have to rewrite the code to make it compile and work:

class LinkedNode<T> {
    var value: T
    var next: LinkedNode?
    
    required init(_ value: T) {
        self.value = value
    }
}

class DoublyNode<T>: LinkedNode<T> {
    weak var previous: DoublyNode?
    override var next: LinkedNode<T>? {
        didSet { (next as! Self).previous = self }
    }
}

And everywhere in the later code every time after referring to next property I have to manually cast it to DoublyNode (when I'm working with DoublyNode) because it always has type LinkedNode.
It's manageable but annoying and ugly.


Solution

  • Let me demonstrate why Swift disallows this. If it did allow you to use Self like that, you could in theory do:

    let doublyNode = DoublyNode(1)
    let linkedNode: LinkedNode<Int> = doublyNode // this assignment should work, right?
    linkedNode.next = LinkedNode(2) // "linkedNode" is of type LinkedNode, so Self is LinkedNode, so I can do this assignment, right?
    

    Now what happens? The didSet of next in DoublyNode gets called, and it tries to access previous of LinkedNode(2). The thing is, LinkedNode doesn't even have a previous property! Therefore, allowing you to use Self this way is unsafe.

    I don't think DoublyNode should inherit from LinkedNode at all. Alexander's answer explains this very well, namely that this violates the LSP. One thing you could do to relate these two classes, is with a protocol:

    protocol LinkedNodeProtocol {
        associatedtype Data
        
        var value: Data { get set }
        var next: Self? { get set }
        
        init(_ value: Data)
    }
    
    final class LinkedNode<Data>: LinkedNodeProtocol {
        var value: Data
        var next: LinkedNode?
        
        init(_ value: Data) {
            self.value = value
        }
    }
    
    final class DoublyNode<Data>: LinkedNodeProtocol {
        var value: Data
        weak var previous: DoublyNode?
        var next: DoublyNode? {
            didSet { next?.previous = self }
        }
        
        init(_ value: Data) {
            self.value = value
        }
    }