LoginSignup
8
5

More than 1 year has passed since last update.

SwiftのClosureにおける `$0` とは結局何者なのか

Posted at

先日、業務でSwiftのClosureについて説明する機会があったのですが、「SwiftのClosureにおける $0 とは何なのか」をうまく説明できなかったので悔しくて調べました。

そもそもClosureってなんだっけ。

Swift.orgによると、下記の定義となっています。

Closures are self-contained blocks of functionality that can be passed around and used in your code.
訳: クロージャーは、コード内で受け渡して使用できる機能の自己完結型のブロックです。

この機能(原文ではfunctionality) という部分が大事で、Closureを利用するとなにか任意の振る舞いを別のどこかに受け渡し、自身のコードの箇所ではない全然別の場所で振る舞いを起こすことができるというのが面白い点だと思っています。
(ここでの 振る舞い はなにか変数を操作したり、成功時のハンドラだったりというようなプログラムとしての動き全般を指しています)

Closureってどう書くんだっけ & $0ってなんだっけ。

SwiftのClosureは下記のように記述することができます

let result: ((Int) -> Bool) = { price -> Bool in
    if price < 1500 {
        return true
    } else {
        return false
    }
}

これだけだとシンプルなので、下記のようにもっと短くすることができます

let result((Int) -> Bool) = { price -> Bool in
    return price < 1500
}

この書き方をする場合、return price <= 1500の改行すらも取り除くことができるので、下記のようにきれいにすることができます :hand_splayed: :thumbsup:

let result((Int) -> Bool) = { price -> Bool in return price < 1500 }

ClosureのShorthand Argument Names

上記を何度も定義から書くのはしんどいため、ここでSwiftが気を利かせてくれて、クロージャーが1行のときは、クロージャー内部で利用することができる $0 系の仮引数を設定してくれます

let result: ((Int) -> Bool) = { return $0 < 1500 }

Swift.org上だと下記のように説明しています

Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names \$0, \$1, \$2, and so on.
訳: Swiftは、インラインクロージャに短縮引数名を自動的に提供します。これを使用して、クロージャの引数の値を$ 0、$ 1、$ 2などの名前で参照できます。

このため、SwiftのClosureでよく使っている $0 は実は、Swiftが気を利かせて作ってくれている 仮引数名なのだよ、という話でした。

補足: $0は衝突します。

(良いか悪いかはおいておいて)closureの中にclosureを書こうとする場合、 Shorthand Argument Names を利用すると名前が衝突してコンパイルできなくなります

let sources = [[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10]]

sources.forEach({ print($0.forEach({ print($0) }) })
// ↑ $0の名前が衝突してコンパイルできなくなる

なので、この場合は素直に引数名を定義してあげましょう

let sources = [[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10],[1,2,3,4,5,6,7,8,9,10]]

sources.forEach({ values -> Void in print(values.forEach({ value -> Void in print(value) })) })

上記の例ではどちらにも引数を定義していますが、 $0 が衝突さえしなければ大丈夫なので、どちらか片方だけでも大丈夫だと思います(多分)

これで明日からコンパイルエラーに困っても多分大丈夫です!

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5