Swift の switch-case は範囲も使えるので大変便利ではありますが、Int 型でその「範囲」の使い方に意外な落とし穴がありました。例えば Playground で下記のコードを書いてみましょう:
let a = 0
switch a {
case Int.min ..< 0:
print("負数")
case 0:
print("零")
case 1 ... Int.max:
print("正数")
default:
fatalError("俺はプログラマをやめるぞ!")
}
まあこれなら結果はお察しの通り「零」が出力されますね。試しに let a = -1
に直してみればそれはまた「負数」が出力されますね。では、試しに let a = 1
に直してみれば結果はどうなるのでしょうか?
理由はいかにも単純で、実は case
文のあとに書く範囲はデフォルトでは Range
にみなされてしまうのです。Swift における Range
と言うのは Range.startIndex ..< Range.endIndex
のように表現されますので、つまり最後は case 1 ... Int.max
と書いているにも関わらず、実際は case 1 ..< Int.max.successor()
、つまり endIndex
は Int.max + 1
で処理されてしまうのです。当然、すでに Int.max
だからそこから更にプラスすると表現できずランタイムエラーになってしまいます。
実はこれ、Stackoverflow にも気づいた人がいたのですがどうやらいかんせんアップルはこれを直す気がなさそうです。ですのでこれはどうにかソースコードで直すしかないのです。まあ一番簡単なやり方として
case 1 ..< Int.max, Int.max:
と、1 ..< Int.max
の Range
を作ってそこにもう一つ Int.max
を追加する方法ですかね。もしくは「こんな書き方じゃあスマートじゃない!」と思うのなら、下記の書き方もできます:
case ClosedInterval(1 ... Int.max):
こう書けば強制的に Range
ではなく ClosedInterval
で case
の範囲を作りますので、これも一つの手です。
まあどのみち、どうせ Int
型なんだから最初から Interval
で範囲作ってくれればいいのにアップル様。
p.s. あともうすでに case
の中に全ての Int
の場合を挙げたにも関わらずそれでも default
書かなきゃいけないアップル様のバカ!