Came across an issue today that seems to violate an expectation I had on how extensions work.
According to the language guide:
Extensions add new functionality to an existing class, structure, enumeration, or protocol type.
Extensions can add new functionality to a type, but they can’t override existing functionality.
Given
struct Foo {
func bar() { print("bar") }
}
As expected, the following extension will produce a compiler error: Invalid redeclaration of 'bar()'
extension Foo {
func bar() { print("bazz") } // compiler error
}
However, if we make Foo
Generic:
struct Foo<T> {
func bar() { print("bar") }
}
As in the previous example, this extension will also generate a compiler error
extension Foo {
func bar() { print("bazz") } // compiler error
}
However, if we specialize the extension, we don't get a compiler error and we can override the functionality of bar()
extension Foo where T == String {
func bar() { print("bazz") } // NO compiler error
}
You can only define this overriding method once, if you try to create another extension on Foo where T == String
, you will get the same Invalid redeclaration of 'bar()'
error.
Initially this doesn't seem totally crazy. Foo<String>
and Foo<Int>
aren't technically the same type, but it seems like a violation of extension design to not override existing behavior. For instance you can do this:
extension Array where Element == Int {
func append(_ newElement: Element) {
print("append is not allowed")
}
func insert(_ newElement: Element, at:Int) {
print("insert is not allowed")
}
}
This completely changes the implementation of the Array
Type, which extensions shouldn't be able to do.
Is this a bug in the compiler or is this the expected behavior? The Language guide doesn't call this out anywhere that I could find. It seems to me like it's a bug.
It seems like maybe the Generic Type function declarations get treated like protocol extensions?
protocol Foo {
associatedtype T
var value: T { get }
func bar()
}
extension Foo {
func bar() { print("Bar") }
}
struct SomeFoo<T>: Foo {
let value: T
func bar() { print("SomeBar") }
}
let fooInt = SomeFoo(value: 1)
fooInt.bar() // Bar
let fooString = SomeFoo(value: "bar")
fooString.bar() // SomeBar
Trying to understand if this override behavior on Generic Types is intended or this is a bit of a loophole that needs to get fixed in a future version.