Swiftを触っていて、割と初歩的なことを認識できていなかったので備考録。

error: ambiguous use

下記のようなクロージャーを引数にする関数を持っているstructを実行すると

struct Music {
    static func play(completion: @escaping () -> Void) {
        print("🎵")
    }
}

Music.play(completion: { (something) in } )

結果は

🎵

になります。ここまでは期待通りの動作です。

次に、このMusic structに

static func play(completion: @escaping (String) -> Void) {
    print("🎵🎵🎵")
}

というString型を引数にするクロージャを引数に指定した同名のメソッドを宣言してみると、下記のようなエラーメッセージが表示されます。

Playground execution failed: error: MyPlayground.playground:10:1: error: ambiguous use of 'play(completion:)'
Music.play(completion: { (something) in } )

() -> Voidに対して、引数を持たないクロージャという認識だったため、上記のエラーで混乱してしまいました。
( そもそも引数を持たないという理解だったら、Music.play(completion: { (something) in } )で動くのがおかしいと思うべきだったかも・・)

Swiftの関数は必ず何らかの引数を取る。

クロージャで指定した() -> Void について考えてみます。
Swiftのモジュールを見ると

public typealias Void = ()

Voidというのは()のエイリアスとなっています。
また、()型というのは空のタプルを表しているので、() -> Void というのは空のタプルを受け取り、空のタプルを返すという事になります。
ドキュメントにも下記のように書かれています。

Functions without a defined return type return a special value of type Void. This is simply an empty tuple, which is written as ().

今回の場合、somethingという型の指定がない引数を取っていたため、このsomethingは()型String型も取れる可能性があり、 コンパイラがメソッドを指定できなかったという事になります。
例えば、上記の処理を下記のように、型指定してあげるとエラーは修正されます。

Music.play(completion: { (r: String) in } )
Music.play(completion: { (r: ()) in } )

まとめ

  • Swiftの関数は必ず、何らかの引数を取り、何らかの返り値を返す。
  • () -> Void が引数を取らないクロージャという理解は良くない。

参考資料