LoginSignup
36
37

More than 5 years have passed since last update.

SwiftのOptionalとType Safety

Posted at

さんざんいろいろな方が書いていますが、SwiftのOptionalについてです。
(Qiita初投稿です)

Optionalとは

Swiftは"Type Safety"な言語です。と、アップルのドキュメントには書かれています。
String型にInt型の代入はできませんし、String型などの通常の型にnilを代入することもできません。

以下のようなコードは静的な型チェックでエラーとなります。

var someValue: Int = nil //エラー

ただし、値がない変数を使用することはかなりの頻度でありますよね。そのような場合に登場するのがOptionalです。変数の型をOptionalにすることでnilの代入が可能になります。

Optionalを指定する方法ですが、変数の宣言時に型の後ろに?をつけます。これでnilの代入が可能になります。

var someValue: Int? = nil //問題なし

このように型をOptionalにすることを“Optional型にラップする”と呼びます。

(このようなOptional型に“ラップする”とは、アップルのドキュメント内では述べられていませんが、Optionalの解除にUnwrapという言葉を使用しているため、この言葉を使用しました。WikipediaのSwiftのページでもこの言葉使われています。)

ちなみに、Optional型の変数や定数に初期値を指定しなかった場合、初期値は自動的にnilになります。

var someValue: Int? //nilがデフォルトで設定される

Optional型を使用するのは”値が入っていない/入らないかもしれない”変数を宣言する場合です。Optionalという言葉そのまま、オプションな変数を指定という考え方ですね。

Forced Unwrapping

先ほど、「通常の型にnilを代入することはできない」と述べました。
一方で「Optional型にするとnilの代入が可能になる」とも述べました。

それでは、Optional型の変数(=nilが入っているかもしれない)を通常の型(=nilの代入ができない)の変数に代入することはできるのでしょうか?

以下のコードで見てみましょう。

var number: Int? = 42
var newNumber: Int = number

一見、問題ないように見えますが、これは静的な型チェックでエラーとなります。
なぜかと言うと、numbernilが入っているかもしれない変数であり、newNumbernilを代入することができない変数だからです。

このような場合、Optionalな変数の末尾に!をつけることでOptional型をアンラップすることができます。

先ほどのnumber変数に!をつけてみましょう。これでエラーは出なくなります。

var number: Int? = 42
var newNumber: Int = number!

!は「ここには値が入っているからOptionalを外したぞ!」と明示するということですが、この値の保証はプログラマが担保するものと思ってよいでしょう。

このように!を使用したOptional型のアンラップは、アップルのドキュメントでは"Forced Unwrapping"と書かれています。"強制解除"という訳し方になるでしょうか。

少しお行儀が悪いので、if文でnilチェックを行ってから使用しましょう。

var number: Int? = 42
var newNumber: Int

if number {
    newNumber = number!
}else{
    println("There isn't value")
}

Optionalな変数をif文に渡すと値が入っていればtrue、値がnilの場合はfalseと判定されます。きちんとnumber変数がnilでないかをチェックしているので比較的安心して使用することができますね。

このように!=アンラップする際はOptionalがnilでないかを必ず確認しましょう。なぜでしょうか?

以下のコードをご覧ください。

var number: Int? = nil
var newNumber: Int = number!

Optional型をアンラップしているため、このコードは静的な型チェックが働きません。しかしランタイムエラーとなってしまいます。これは非常に危険ですよね。

このような場合、プログラマがnilでないことを保証する必要があります。

とは言え、よほどスコープの狭い変数以外は完璧に保証することは難しいですよね。ですので!を使用する際はif文によるnilチェックを必ず行いましょう。

Optional Binding

if文によるチェックの方法をさらに安全にさせたものに"Optional Binding"というものがあります。

var number: Int? = 42
var newNumber: Int

if let validNumber = number {
    newNumber = validNumber
}else{
    println("There isn't value")
}

Optionalな変数のnilチェックを行った後に、一時的な変数あるいは定数を使用する方法です。

validNumberにはnilでないことが確認された上で値が代入されています。!によるOptional型のアンラップも使用されておらず安全に使用できます。

なるべくOptional Bindingを使用して、ランタイムエラーを引き起こす可能性もある!によるOptional型のアンラップは使用しない方が良いのではないかと思います。

("Optional Binding"は日本語のWikipediaのSwiftでは"Optional束縛"と書かれています。この表現はニュアンスは伝わりやすい気がします。)

関数の戻り値にOptionalを使用する

最後に関数の戻り値がOptionalだった場合はどうなのか見てみましょう。

func helloLanguage(name: String) -> String?{
    if name == "Swift" {
        return "Hello " + name
    }else{
        return nil
    }
}

Optional型のString?が戻り値の型です。nilが返ってくる可能性がありますね。

helloLanguageから返ってきた値は通常の型の変数には代入できません。戻り値がOptional型で、nilが返ってくる可能性があります。

var hello: String  = helloLanguage("Swift") //エラー

nilでない値が返ってくることを保証できるのであれば、以下のように!によるOptional型のアンラップも使用できます。

var hello: String  = helloLanguage("Swift")!

ただこのような場合はOptional Bindingなどでnilチェックをした方が安全でしょう。

以下のようにhelloLanguage関数から返ってきた値を代入する変数もOptional型であれば問題ありません。

var hello: String? = helloLanguage("Swift")

「どうしてもhelloはOptional型ではダメなんだ!」という場合は、helloLanguage関数の方を修正(戻り値の型をOptionalでない通常の型に)する方がSwiftの"Type Safety"な言語仕様を守ることができるのではないかと思います。

まとめ

「通常の型にnilは代入できない」、そして「Optional型はnilの代入が可能」であるということがポイントです。

さらに、

  • if文やOptional Bindingでnilのチェックを行う
  • !によるOptional型のアンラップは極力使用しない

ということがOptionalを安全に使用して、Swiftの"Type Safety"な言語仕様を生かす方法だと思います。

36
37
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
37