Após a conversão do Swift 2.2 para 3.0 minha extensão Array
não compila mais, porque contém uma chamada para a function de biblioteca padrão global min(T,T)
e mostra extra argument in call
erro do compilador extra argument in call
.
Aqui está uma maneira simples de reproduzir o erro:
extension Array { func smallestInt(first: Int, second: Int) -> Int { return min(first, second) // compiler error: "Extra argument in call" } }
Eu recebo o mesmo erro ao adicionar a mesma function a uma extensão do Dictionary
, enquanto o mesmo código compila muito bem em uma extensão de outros tipos (por exemplo, String
ou AudioBuffer
):
Olhando para a documentação do Array
and Dictionary
, eu acho que existem methods de instância em Sequence
nomeados public func min() -> Element?
e public func min(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
. Enquanto ambos String
e AudioBuffer
não possuem nenhum tipo de function min(...)
.
É possível que essa seja a razão pela qual não posso chamar a function global? O compilador não pode distinguir entre func min(T,T)
global func min(T,T)
e self.min(...)
embora tenham assinaturas completamente diferentes?
isso é um erro ou uma característica? O que estou fazendo de errado? Como posso chamar min(T,T)
corretamente dentro de uma extensão Array
?
Não vejo razão para que o compilador não consiga resolver essa chamada de function, portanto, eu consideraria um bug (ela já foi preenchida – consulte SR-2450 ).
Parece ocorrer sempre que se tenta chamar uma function de nível superior com o mesmo nome, mas assinatura inequivocamente diferente para um método ou propriedade acessível a partir do mesmo escopo em um determinado tipo (instância ou estática).
Um exemplo ainda mais simples seria:
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 } }
Até ser corrigido, uma solução simples seria prefixar a chamada com o nome do módulo no qual ela reside, a fim de desambiguar que você está se referindo à function de nível superior, em vez da instância um. Para a biblioteca padrão, isso é Swift
:
extension Array { func smallestInt(first: Int, second: Int) -> Int { return Swift.min(first, second) } }
No Swift 4, o compilador tem um diagnóstico melhor para esse erro (embora o fato de ainda ser um erro seja um bug do 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) } }
Embora o interessante seja que o compilador agora também avisará sobre a tentativa de chamar um método de biblioteca padrão com o mesmo nome de uma function de nível superior stdlib:
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() } }
Nesse caso, como o aviso diz, você pode silenciá-lo usando um self.
explícito self.
:
extension Array where Element : Comparable { func smallest() -> Element? { return self.min() } }
Embora o que é realmente curioso sobre este aviso é que ele não parece se estender para funções definidas não-stdlib:
func foo(_ a: Int) {} struct Foo { func foo() {} func bar() { foo() // no warning... } }