[swift-evolution] Warning when "overriding" an extension method that's not in the protocol?

Chris Eidhof chris at eidhof.nl
Thu Dec 10 16:43:24 CST 2015


Let's consider some subtle behavior when dealing with protocol extensions. We have a simple protocol:

protocol Shareable {
    var myDescription: String { get }
}

Using an extension, we can add a quick function `share` that prints the social media description to the command line:

extension Shareable {
    func share() {
        print("Sharing: \(self.myDescription)")
    }

    func linesAndShare() {
      print("----------")
      share()
      print("----------")
    }
}

We can make string conform to `Shareable` by implementing the `myDescription`. This is all we need to do, we then automatically get the `share()` method for free. However, if we choose to, we can also create a custom variant of the `share` method. For example, if we make `String` conform to `Shareable`, we could do it like this:

extension String:  Shareable {
    var myDescription: String { return self }
    
    func share() {
        print("Special String Sharing: \(self.myDescription)")
    }
}

Now, if we create a string and call `share()` on it, it will use our custom `share` method:

"hello".share()
// Prints "Special String Sharing: hello"

However, if we treat "hello" as a `Shareable` value, and then call `share()`, we get a very different result:

let hello: Shareable = "hello"
hello.share()
// Prints: "Sharing: hello"

Things get even more interesting. What happens if we call `linesAndShare` directly on a `String`?

"hello".linesAndShare()
// Prints: 
//
// ----------
// Sharing: hello
// ----------

Coming from dynamic languages, you might be very surprised by the output of the last two examples. Even though we defined `share` on `String`, it did not override our default implementation. If we want to allow types conforming to `Share` to provide their own custom implementations, we need to specify `share()` in the protocol as well:

protocol Shareable {
    var myDescription: String { get }
    func share()
}

Now, we can still provide a default implementation of `share()`, yet allow other types to override the `share()` function. 

If you've made it all the way through, I want to propose that we add a warning in case you override a protocol extension method that's visible in your scope (and has exactly the same type). For example, implementing `share()` in String could trigger a warning. In my experience, it's confusing that it looks like you're overriding it, but you're not really. I strongly believe the current behavior is correct, yet, it can be quite confusing, especially coming from ObjC.

Chris



More information about the swift-evolution mailing list