After converting from Swift 2.2 to 3.0 my Array
extension does not compile anymore, because it contains a call to global standard library function min<T>(T,T)
and shows compiler error extra argument in call
.
Here's a simple way to reproduce the error:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return min(first, second) // compiler error: "Extra argument in call"
}
}
I get the same error when adding the same function to an extension of Dictionary
, while the exact same code compiles just fine in an extension of other types (e.g. String
or AudioBuffer
):
Looking at the documentation of Array
and Dictionary
, I find that there are instance methods on Sequence
named public func min() -> Element?
and public func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
. While both String
and AudioBuffer
do not have any kind of min(...)
function.
Is it possible that this is the reason why I can't call the global function? The compiler can't distinguish between global func min<T>(T,T)
and self.min(...)
although they have completely different signatures?
Is this a bug or a feature? What am I doing wrong? How can I call min(T,T)
correctly inside an Array
extension?
I see no reason why the compiler shouldn't be able to resolve this function call, therefore I would consider it a bug (it has already been filed – see SR-2450).
It seems to occur whenever attempting to call a top-level function with the same name, but unambiguously different signature to a method or property that's accessible from the same scope in a given type (instance or static).
An even simpler example would be:
func foo(_ a: Int) {}
struct Foo {
func foo() {} // or static func foo() {}, var foo = 0, static var foo = 0
func bar() {
foo(2) // error: argument passed to call that takes no arguments
}
}
Until fixed, a simple solution would be to prefix the call with the name of the module in which it resides in order to disambiguate that you're referring to the top-level function, rather than the instance one. For the standard library, that's Swift
:
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
return Swift.min(first, second)
}
}
In Swift 4, the compiler has a better diagnostic for this error (though the fact that it's still an error is a bug IMO):
extension Array {
func smallestInt(first: Int, second: Int) -> Int {
// Use of 'min' refers to instance method 'min(by:)'
// rather than global function 'min' in module 'Swift'
// - Use 'Swift.' to reference the global function in module 'Swift'
return min(first, second)
}
}
Although what's interesting is that the compiler will now also warn on attempting to call a standard library method with the same name as a stdlib top-level function:
extension Array where Element : Comparable {
func smallest() -> Element? {
// Use of 'min' treated as a reference to instance method in protocol 'Sequence'
// - Use 'self.' to silence this warning
// - Use 'Swift.' to reference the global function
return min()
}
}
In this case, as the warning says, you can silence it by using an explicit self.
:
extension Array where Element : Comparable {
func smallest() -> Element? {
return self.min()
}
}
Although what's really curious about this warning is it doesn't appear to extend to non-stdlib defined functions:
func foo(_ a: Int) {}
struct Foo {
func foo() {}
func bar() {
foo() // no warning...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With