Shoichi Matsuda's diary

このブログは移転しました。 https://shoma2da.net/ が新しいブログです。

Swiftことはじめ:String?のクエスチョンマークって何?

Swift出ましたね!

WWDCで突然の言語発表で驚きです。

f:id:shoma2da:20140603232720p:plain

 

無料のドキュメントが提供されており、ざっと読んでみた限り最近の言語のエッセンスを色々と取り込んだ良い意味で特徴のない(かなり書きやすそうな!)言語という印象を受けました。

 

今回はそんなSwiftの中のoptional valueなどと呼ばれている言語仕様について取り上げていきます。

 

どれのこと?

ドキュメント中に出てくる以下の様な記述です。

var optionalString: String? = "hello"
optionalString = nil
println(optionalString)

 

このString?、なんのことだかわかりますか?

 

このクエスチョンマーク、どうやって使うの?

使い方を見ていきましょう。

 

まず普通の変数宣言で以下の様に書いてみましょう。

var normalString = "aaa"
normalString = "bbb" //普通に代入できる
normalString = nil //コンパイルエラー!

なんとnilの代入ができません!

 

これは次のように書いたのと全く同じ意味になります。 

var normalString:String = "aaa"
normalString = "bbb" //普通に代入できる
normalString = nil //コンパイルエラー!

normalStringの型はStringです。

 

ではnilを代入したい場合はどうするのでしょうか?

そんな時に出てくるのがこのクエスチョンマークです。

var optionalString:String? = "aaa"
optionalString = "bbb" //普通に代入できる
optionalString = nil //代入できるようになった!

optionalStringの型はString?です。

 

このようにSwiftではデフォルトでは変数にnilが入らないようになっており、

nilを代入したい場合は型に?を付けた型で宣言します。

 

こうしたクエスチョンマーク付きの型は変数だけでなく関数の引数や返却値としても使用できます。

func optionalReceiveMethod(string:String?) {
    println("received value is [\(string)]")
}

optionalReceiveMethod("hello")
optionalReceiveMethod(nil) //関数の引数がStringだとエラーになります
func optionalReturnMethod() -> String? {
    return nil //返却値の型がStringだとエラーになります
}

 

次に変数の使い方を見て行きましょう。

使うときには?!が重要になります。

var noarmalString:String = "aaa"
println(noarmalString.uppercaseString)

var optionalStringFirst:String? = nil
//println(optionalStringFirst.uppercaseString) コンパイルエラー!
println(optionalStringFirst?.uppercaseString)
//?が付いたところを評価してnilならそこでメソッド呼び出しをやめて結果をnilとする

var optionalStringSecond:String? = nil
println(optionalStringSecond!.uppercaseString)
//!の場所はnilだろうがなんだろうが無理やり実行する→ここだとクラッシュします…

!を使ってしまうとoptional valueを使う意味がほとんどなくなってしまうので基本的には使わない方が無難でしょう。(詳細は後述します)

 

また以下のようにifで囲めばnilでないことがわかるので?!を使う必要はなくなります。

var optionalString:String? = "aaa"
if optionalString {
    println(optionalString)
}

 

使い方はここまでを把握できていれば十分かと思います。

 

なんでこんな言語仕様があるの?

結論から言ってしまうと「コンパイル時点でnilをチェックするため」です。

 

今までnilに起因したクラッシュが発生したことはありませんでしょうか?

おそらくみなさん経験していることでしょう。

 

Objective-Cでは値がないことをチェックするにはいわゆるnullチェックをしていたかと思います。

※厳密にはNSNullnilnullなどでそれぞれ対応は異なったりしますが話を簡単にしています。

 

しかし通常のnullチェックの場合、以下の様な欠点がありました。

  • nullになり得るかどうかをプログラマが考える必要がある→対応忘れが頻繁に起きる
  • ほんとにnullになるかどうかは実行時にわかる→ストア配布後などにテスト漏れによるnilに起因したクラッシュが発生する

 

こうした状況に対処できるのが今回説明しているoptional valueです。

上記の欠点についてoptional valueなら次のことが言えます。

  • nullになり得るかどうかは型で把握しているのでコンパイラが判断できる
  • ほんとにnullになるかどうかはコンパイル時点でわかる

 

つまり今まではプログラマが考えたり実行してテストしていたものをコンパイラという機械にまかせてしまうことができます。

 

さきほど!は使わない、と言いましたが!を使うとコンパイラによって得られる恩恵を全て無視してしまっていることがわかるかと思います。

良くないですね。

 

まとめ

optional valueは「値がない場合」の対応をほぼコンパイラに任せたような言語仕様でした。

 

nullについてはnull参照の考案は10億ドル単位の過ち?などと言われることもあるように旧来の言語ではなかなか苦戦していた部分でした。

今回はSwiftで説明しましたが他の言語を見てみると、KotlinやCoffeeScriptではSwiftと似たような機能が既に取り入れられています。

またScalaなど関数型の色が濃い言語ではMaybeモナドによる解決策なども図られています。

 

最近は自動テストやCIなどこれまでプログラマが行ってきたことはどんどんと機械に任せられていっています。

様々な言語の仕様でもその流れに沿っていて、メモリ管理・暗黙の値・遅延評価・今回のようなnull対応など、コンパイラや実行マシンに任せる比重もどんどんと増えているように感じます。

 

人にしかできないことがより強く重視されていくんでしょうね!楽しみ!!