次のコードを実行すると、 a
に nil
が代入されると考える人は多いと思います。
let a: String? = nil
しかし、 Swift に nil
という値は存在しません。存在するのは nil
というリテラルだけです。 a
に格納される値は Optional.None
であって nil
ではありません。
多くの言語ではリテラルはある型の値やインスタンスを表しますが、 Swift のリテラルは特定の型に紐付いていません。
本投稿では Swift のリテラルの仕組みについて説明し、 Swift に nil
という値が存在しないことを示します。
Swiftと多くの言語でのリテラルの違い
多くの言語では、リテラルはそれ自体がどの型の値であるかを表しています。例えば、 Java では次のようにリテラルに対して型が一意に決定されます。
123 // int
123L // long
123.0 // double
123.0F // float
true // boolean
'a' // char
"ABC" // String
しかし、Swiftではそうではありません。 123.0
は Float
にもなりますし、 Double
にもなります。
let f: Float = 123.0 // Float
let d: Double = 123.0 // Double
もっと言えば、 123.0
は 他のどのような型のリテラルにでもなれます。
例えば、次のような Extension を作れば String
型の変数に 123.0
というリテラルを代入できるようになります。
extension String : FloatLiteralConvertible /* Float リテラルを代入可能にする */ {
public init(floatLiteral value: FloatLiteralType) {
self = "\(value)"
}
}
let s: String = 123.0 // s には "123.0" という文字列が代入される
Swift の 123.0
は Float
型のリテラルではなく FloatLiteralConvertible
を実装したすべての型に代入可能なリテラルなのです。
Swift のリテラルは型を持ちません。対応する LiteralConvertible
が実装された型の変数であれば何にでも代入できます。
NilLiteralConvertible
Swift の nil
はリテラルの一種です。 123.0
を代入可能にする FloatLiteralConvertible
があったように、 nil
を代入可能にする NilLiteralConvertible
というプロトコルがあります。
また、 Swift では String?
は Optional<String>
のシンタックスシュガーです(詳しくはこちら)。 let a: String? = nil
のように 'nil' が代入できるのは、 Optional
が特別な型だからではなく NilLiteralConvertible
を実装しているからです。
Optional
の他に NilLiteralConvertible
を実装した型に UnsafePointer
があります。そのため、次のようなコードが書けます。
let p: UnsafePointer<Int> = nil // Optional でないけど nil を代入できる
println(p) // 0x0000000000000000
p
は Optional
でないのに nil
が代入できてしまいました。このとき p
に代入された nil
と、 Optional
に代入される nil
には何の関係もありません。前者は C 言語で言うところの NULL ポインタで、後者は Optional.None
です。 NilLiteralConvertible
はもし nil
というリテラルが代入されたらどうするかを定義するだけ で、それをどのような値として解釈することもできます。
極端な話、 nil
が代入されたら 42
とするという意味のない定義をすることもできます。
extension Int : NilLiteralConvertible /* NilLiteralConvertible */ {
public init(nilLiteral: ()) {
self = 42 // 「生命、宇宙、そして万物についての究極の疑問の答え」
}
}
let n: Int = nil
println(n) // 42
このことからも Swift の nil
が値ではなく単なるリテラルであることがわかる と思います。
ちなみに、下記のコードで nil
と出力されるのは a
に nil
が代入されているからではなく、 Optional.None
の debugDescription
プロパティが "nil"
という String
を生成し、それを出力しているだけです。
let a: String? = nil // a に Optional.None が代入される
println(a) // Optional.None の debugDescription の値である "nil" が出力される
println(Optional<String>.None.debugDescription) // nil
色々なLiteralConvertible
その他にも、 Swift には次のような LiteralConvertible
が宣言されています(これがすべてではないです)。
ArrayLiteralConvertible
BooleanLiteralConvertible
DictionaryLiteralConvertible
FloatLiteralConvertible
IntegerLiteralConvertible
StringLiteralConvertible
ArrayLiteralConvertible
や DictionaryLiteralConvertible
は Array
や Dictionary
を自作(クラス版を作るとか)する場合などに、 IntegerLiteralConvertible
は任意精度整数を作った場合などに便利そうです。
多くの言語では自作の便利クラスを作っても、組み込み型のようなリテラルのサポートをうけられずどうしても貧弱になってしまいます。 Swift のように LiteralConvertible
方式であれば、組み込み型と遜色のない便利な自作クラスを作ることができそうです( Swift には Operator Function や Subscript もあるので、演算子を定義したり [i]
でアクセスできるようにしたりして、組み込み型と同じように振る舞わせることもできます)。
まとめ
- Swift には
nil
という値は存在せずリテラルがあるだけ - Swift のリテラルは型と結びついていない
-
LiteralConvertible
がリテラルを値に変換する -
nil
の意味はNilLiteralConvertible
の実装で決まる-
Optional
の場合はOptional.None
-
UnsafePointer
の場合は0x0000000000000000
-
- 自作クラスに
LiteralConvertible
を実装すると便利そう