次のコードを実行すると、 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を実装すると便利そう