AV8R
AV8R

Reputation: 63

Is swift extension default implementation solved on compile or runtime?

I'm wondering is there a way to work with protocol default implementations in polymorphic style. Example

protocol RockInterface {
}

extension RockInterface {
    func foo() {
        print("You Rock")
    }
}

extension RockInterface where Self: Metal {
    func foo() {
        print("Metal")
    }
}

extension RockInterface where Self: Grunge {
    func foo() {
        print("Grunge")
    }
}

class Rock: RockInterface {
    init() {
        foo()
    }
}

class Metal: Rock {

}

class Grunge: Rock {

}

let rock = Rock()       //prints "You Rock"
let metal = Metal()     //prints "You Rock"
let grunge = Grunge()   //prints "You Rock"

I expected Metal() to print "Metal", and Grunge to print "Grunge". But it seems that default implementations are solved at compile time instead of runtime. Is my assumption right of wrong? How could I get expected behavior?

Upvotes: 0

Views: 291

Answers (1)

Cristik
Cristik

Reputation: 32904

The are at least two factors that contribute to the behaviour you see, some within your control, and some not.

  1. functions that are not part of the protocol requirements are statically dispatched. If you want dynamic dispatch, then you'll need to add the method to the protocol declaration:
protocol RockInterface {
    func foo()
}
  1. however, the above won't solve your problem, since subclasses inherit the protocol witness table of the parent class. See this excellent answer for more details about this.

I'd also argue you design is a good one, since you tightly coupled the protocol and the classes that conform to that protocol. If you really need the behaviour you described, then one solution would be to drop the protocol extensions, and implement the foo method within each class:

protocol RockInterface {
    func foo()
}

class Rock: RockInterface {
    init() {
        foo()
    }
    
    func foo() {
        print("You Rock")
    }
}

class Metal: Rock {
    override func foo() {
        print("Metal")
    }
}

class Grunge: Rock {
    override func foo() {
        print("Grunge")
    }
}

let rock = Rock()       //prints "You Rock"
let metal = Metal()     //prints "Metal"
let grunge = Grunge()   //prints "Grunge"

Upvotes: 0

Related Questions