- Xcode 6.1.1
- Playground
ハマった箇所
RGB値などを処理するコードを書いていたのですが、以下のような処理が実行時にエラーとなりました。
// 適当
let a: UInt8 = 230
// 計算&UInt8の上限を超えないように抑える的な処理
let b: UInt8 = min(255, Int(a) + 50)
min
関数の第一引数はリテラルなので型は曖昧ですが、第二引数はInt型+リテラルなのでジェネリクス関数のmin<T>
はTの型をInt
と判断する だろう と思って書いたコードです。
しかし実際にはmin
関数は引数をUInt8
型として受け取っているようで、第二引数が230 + 50 = 280
で255
を超えてしまっているため、オーバーフロー判定により実行時エラーとなっていると思われます。
- 実際には、ある関数の引数へ、min関数を使った結果を渡している所で発生しました。
検証
_stdlib_getTypeName
という関数を使うと型の情報が得られるようですので、それを使ってどんな型として処理されているのかを見てみました。
まずは以下のように定数b
の型を未指定にしてみます。
let a = UInt8(100)
func gen<T>(value: T) -> T {
println(_stdlib_getTypeName(value)) // "_TtSi"
return value
}
let b = gen(Int(a) + 50)
この時は"_TtSi"
と出ますので、期待通りInt
型として動いています。
しかし、b
の型をいじると...
let a = UInt8(100)
func gen<T>(value: T) -> T {
println(_stdlib_getTypeName(value)) // "_TtVSs5UInt8"
return value
}
let b: UInt8 = gen(Int(a) + 50)
今度は"_TtVSs5UInt8"
というようにUInt8
型として処理していることがわかります。
左辺(代入先)の型に、ジェネリクスの型が影響を受けるって事ですね。
とはいえ、少なくともInt(a)
だけはInt
型のはずで、そこからどうやってUInt8
型になってしまったのか、意味不明です。
勝手にコンバージョンしてるとしてもおかしな話です。
回避策
今回の件で言えばジェネリクス関数の戻り値をコンバージョンしてやれば期待通りになります。
// 適当
let a: UInt8 = 230
// 計算&UInt8の上限を超えないように抑える的な処理
let b: UInt8 = UInt8(min(255, Int(a) + 50)) // 255
実行時かつオーバーフローするケースでのみ発生するため、潜在的なバグになりえます。
お気をつけください。