2014/08/06 追記
この記事の内容は古くなりました。
Xcode6 Beta5からはLogicValue
はBooleanType
に変更されました。
また、Optional
型はBooleanType
に準拠しなくなったため、以下はコンパイルエラーになるようになりました。
let str: String? = "あいうえお"
if str { // コンパイルエラー
println("str=\(str)")
}
まだImplicitlyUnwrappedOptional
の方はBooleanType
に準拠しているようですが...
はじめに
※TwitterやQiitaで疑問に感じられている方がいらっしゃるようなので、日頃薄々感づいていた事と憶測をミックスしてお送りします。
- Xcode6 beta4
Swiftでは条件式の結果は論理値にする必要がある
Swiftにおいて、ifやwhile、三項演算子の条件式の結果は、Bool値にしないとコンパイルが通らなくなりました。
let i = 1
if i { // コンパイルエラー
println("true")
}
if i != 0 { // こっちはOK
println("true")
}
Playground execution failed: error: <EXPR>:10:4: error: type 'Int' does not conform to protocol 'LogicValue'
で、これはそういう物なんだと覚えておいて、いざ次のようなコードに出会うと「えっ??」ってなると思います。
let str: String? = "あいうえお"
if str {
println("str=\(str)")
}
str=Optional("あいうえお")
Bool値じゃないのに何で??
Swiftで条件式を評価するカラクリ
Swiftでは条件式の結果値の型が、LogicValue
プロトコルに準拠(実装)している必要があります。
protocol LogicValue {
func getLogicValue() -> Bool
}
ご覧の通り、getLgicValue()
という関数が一つだけ定義されています。
ifなどの条件式では、条件式の結果の値に対して、この関数を呼び出し、その戻り値がtrueかどうかを評価しているようなのです。
Bool
型もこの関数を実装しています。
extension Bool : LogicValue {
func getLogicValue() -> Bool
init(_ v: LogicValue)
}
おそらくreturn self
しているだけでしょう。
Optional
型についても、この関数を実装しています。
enum Optional<T> : LogicValue, Reflectable, NilLiteralConvertible {
(略)
/// Allow use in a Boolean context.
func getLogicValue() -> Bool
(略)
}
おそらくreturn self != nil
だとか"Noneじゃない"だとかそういう事が実装されているんだと思います。
というわけで、条件式は「論理値で判断している」、もっと詳しく言えば「getLogicValue()の結果で判断している」という事ですね。
これがOptional型でもエラーにならないワケです。
諸先輩方に怒られそうな実装
LogicValue
プロトコルにさえ準拠していれば良いので、やろうと思えばObjective-C(C言語)の時みたいな事もできます。
extension Int : LogicValue {
public func getLogicValue() -> Bool {
return self != 0
}
}
let i = 1
if i { // ←これ
println("true")
}
true
こういったC言語風の書き方が出来なくなっているのには理由が有ると思いますのでオススメはしません。
何故か、という事情は詳しくありませんのでどなたか教えてください。
(C#など他の言語でも論理値を強制されるものがありますね。)