Swiftの _
(アンダースコア)の絡んだネタをいくつか書いてみます。
戻り値を _
で受けて破棄
Swift 3では、以下のように書くと、Result of call to 'f()' is unused
という警告が発生します。
func f() -> String {
return ""
}
f()
(※Playgroundでは戻り値が評価されて右のエリアに表示されるようになっているため警告は発生しません。実コード上での確認が必要です。その他、Playgroundの動作はたまに怪しいことがあるので違和感あった際も同様の確認をお勧めします。)
次のように_
で受けると警告が消えます。
_ = f()
あまり慣れていないと、次のように書くのが普通な気がしてしまいますが、_
は特別扱いされていて、let
は省略可能です。(すでにvar _
で宣言済みの変数に入れて葬るイメージ?)
無くても良いものは書かない方が良い(書いたメリットが明確な場合は別として)と思っているので、基本上の書き方が良いと思っています。
let _ = f()
他の言語では規約ベースで_
や_
で始まる識別子で受けて不使用であったりプライベートなものであることの目印にすることが多くて(後者はSwiftでもまあまあやると思いますが)、これが言語仕様に含まれているのはより確実なコードが書きやすくてありがたいです。
また、以下のように何回でも使えます。
_ = f()
_ = f()
破棄用なので、次のように書くと、error: '_' can only appear in a pattern or on the left side of an assignment
というコンパイルエラーが発生します。
print(_)
“An underscore (_) parameter is explicitly ignored and can’t be accessed within the body of the function.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3).” iBooks. https://itun.es/jp/jEUH0.l
もちろん、通常の識別子を使った場合は以下のようにすると2つ目でコンパイルエラー発生します。
let x = f()
let x = f()
ちょっと蛇足ですが、Swiftはスコープがネストされれば同じ識別子使えたりします。
特に静的言語の中ではこう書ける言語少数派な気がします。
下の例はややこしくなってあまり良くない例ですが、このおかげで書きやすい場面は多く、個人的には気に入っている仕様です。
let 🍎 = "りんご"
do {
let 🍎 = "リンゴ"
print(🍎) // リンゴ
}
print(🍎) // りんご
戻り値不使用のケースの別解(@discardableResult
)
Swift 3では、次のように@discardableResult
を関数定義に添えると、戻り値不使用でも警告が発生しなくなります。
@discardableResult func f() -> String {
return ""
}
f()
Swift 2では、デフォルトでは戻り値不使用でも警告は発生せず、@warn_unused_result
を添えると警告が発生する、という逆の挙動でした。
プログラマーのうっかりミスを防ぎやすくなるので、Swift 3の方が良いですね。
参考: swift-evolution/0047-nonvoid-warn.md at master · apple/swift-evolution
こういった警告は、特に以下のケースで、Bのようにsort
を使うべきなのにうっかりAのsorted
を使ってしまい戻り値を受け取らなかった時にそのミスに気付けて素晴らしいです。
var array = [2, 3, 1]
// A: array自体はそのままで、戻り値が [1, 2, 3]となる。戻り値を受けてないので警告発生。
array.sorted()
// B: array自体を[1, 2, 3]に変更
array.sort()
好きな方使えば良いというわけではなく、自身で定義しているなどで書き換え可能かつ戻り値を破棄しても通常問題無いものであるケースのみ、@discardableResult
を添えて定義して、それ以外は利用箇所にて個別に_
で受けて破棄、というのが良いと思っています。
不使用変数を _
で隠す
上の話と似てますが、_
で不使用変数を隠すこともよくします。
こういう関数があったとして、やあ( ´・‿・`)
のようなどうでも良い値が渡ってきても不要なので破棄したいとします。
func f2(closure: (String) -> ()) {
closure("やあ( ´・‿・`)")
}
この場合は、こう書いてもstr
が不使用と怒られることはありませんので、このままでも特に問題ありません。
f2 { str in
}
ただ、間違えてstr
の値を使ってしまうミスを確実に防ぎたい場合は、次のように_
で受けると良いです。
f2 { _ in
// `print(_)`ももちろん不可能
}
しかし、あとでやはり使おうとなった時にどのような値が来ているのかの情報が失われて定義を再確認するなど(特にパラメータの数が多いなど複雑な時)、必ずしも良いことばかりでも無いため、以下のように_str
など_
始まりとして何となく不使用ということを明示するだけにとどめておくパターンもあります。
この場合、単なる規約ベースのやり方なので、うっかり使ってしまうミスは防げませんので、状況によりけりですね。
f2 { _str in
// `print(_str)`と書くと`やあ( ´・‿・`)`と出力されてしまう
}
_
で区切って数値を見やすく
このように、任意の桁に_
を挟むことで、見やすく出来ます。
let intMax = 2_147_483_647
挿入箇所も数も自由なので、このようにして見づらく嫌がらせすることも出来ます( ´・‿・`)
let intMax = 2_147_483________________________________________________________________________________________________________________________________________________________________647
コード上の見た目だけの違いで、扱いは let intMax = 2147483647
と書いた時と全く同じです。
“Underscores (_) are allowed between digits for readability, but they are ignored and therefore don’t affect the value of the literal. Integer literals can begin with leading zeros (0), but they are likewise ignored and don’t affect the base or value of the literal.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3).” iBooks. https://itun.es/jp/jEUH0.l
この書き方に対応した言語は少なめな印象でしたが、最近は増えてきています。
- Ruby: 対応
- Python: 3.5までは非対応
- 3.6で入った(
_
を2つ以上連続させることはできない)
- 3.6で入った(
- Go: 非対応
- C#: 6までは非対応
引数ラベルの省略
こちらは特に当たり前に使っていると思いますが、以下の時にvalue
ラベルを省略したい場合は、
func f3(value: Int) {
}
f3(value: 1)
次のように書きます。
func f3(_ value: Int) {
}
f3(1)
ラベル付きの方は、以下のショートハンドと捉えると_
を指定した時の挙動が理解しやすいですね。
(以下はラベルとパラメーター名を別にしたい時の使い方なのでこのように同名の場合は、Extraneous duplicate parameter name; 'value' already has an argument label
という警告が発生しますが)
func f3(value value: Int) {
}
f3(value: 1)
そういえば、Swift 2まではラベル周りの挙動がちぐはぐでしたが、Swift 3で解消されて良かったですね。
swift-evolution/0046-first-label.md at master · apple/swift-evolution
他にも破棄したい場合の細かい使い方がありますが、本質的に同じなので省略します。
The Swift Programming Language (Swift 3.0.1) by Apple Inc. on iBooksでunderscore
で検索すると、利用例や仕様についての記載を確認出来ます。
_
について書いてて連想しましたが、$
もちょっと特別な感じになる予定です。
swift-evolution/0144-allow-single-dollar-sign-as-valid-identifier.md at master · apple/swift-evolution
こちらについては 🎃Swiftレター #5🎃 – Swift・iOSコラム – Medium に書いたので興味あればご覧ください。