はじめに
プログラミングにはカタカナ語が多すぎる!!!!!
Swiftをやってて、これらのエラーに当たったことはありませんか?
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'Coalesce using '??' to provide a default when the optional value contains 'nil'Force-unwrap using '!' to abort execution if the optional value contains 'nil'
で、どういう意味かと思って調べてみたものの、「オプショナル」だとか「アンラップ」だとかのカタカナ語を前にして挫けたことありません?
それで結局、よく分からずにFixを押して、「なんでか知らんけどエラー消えたしいいや」ってなってませんか?
この記事を頑張って読めば、これらのエラーの原因がちゃんと分かるようになります。
注意書き
-
?と!の基礎だけを簡単に理解できるようにしたために、紹介しきれなかった概念などが少しあります。 -
この記事は、以下の内容を分かっている前提で話を進めていきます。
- 変数や型について(
IntやStringなど) -
if文などの基礎的な文法
- 変数や型について(
-
「カタカナ禁止」を謳ってはいますが、「エラー」などの最低限のカタカナ語は使います。お許しを…。
まずは前提知識から
空っぽ(nil)という概念
冒頭のエラーについて理解するには、まず初めにnilという概念について理解する必要があります。
nilというのは、変数の中が空っぽだよ、何も入ってないよということを表す概念です。
少し強引ですが、「nilについてよう分からん」ってなったら、とりあえず頭の中でnilを「空っぽ」と読んでください。だいたい合っているはずです。
★重要★ nilは「空っぽ」を表す。
nilの使いどころ
例えば、会員登録の際のデータとして、任意で年齢を知りたいという場合について考えてみます。(「任意で」というのがとても重要です。)
年齢は間違いなく整数ですから、データはInt型の変数で持っておけば良さそうですね。
ところが、「任意で知りたい」というのが厄介です。
年齢の入力が任意ということは、「年齢の欄に何も入力されない」という場合も考えられるわけです。
そこで、年齢の欄に何も入力されていなければ、変数の中身を、「空っぽ」であることを表すnilにしておきます。
こうすることで、「年齢が入力されなかった」ということを、nilによって知ることができるのです。
「普通の変数」も見ておこう
実際にプログラムでnilを使う手順について説明する前に、まずは前提知識として、皆さんがいつものように使っている「普通の変数」について考えてみます。
Swiftにおいて、「普通の変数」には必ず何かしらの「値」が入っていなければなりません。
(ここで言う「値」というのは、例えばString型の変数では何かしらの文字列、Int型の変数では何かしらの数字…などといった具合です。)
つまり、Swiftの「普通の変数」において、「変数の中身が空っぽ」というようなことは想定されていないのです。
そのため、Swiftでnilを扱うには、その変数を宣言するときに、「普通の変数」を使うときとは違うことをしなければなりません。
nilを代入できる変数を使う
変数にnilを代入するには、型の名前の後ろに?をつけます。
let hoge: Int = 0 //空っぽ(nil)を代入できない🙅♂️
let fuga: Int? = nil //空っぽ(nil)を代入できる🙆♂️
上の「例1」では、fuga:の後ろのIntにくっついている?がそれです。
?をつけることで、「nilも代入できる」ということが分かりやすくなりますね。
ちなみに!
変数名になっているhogeやfugaというのは、「何でも良い、特別な意味のない名前」で、よくサンプルのプログラムなどで使われているものです。
このような「意味のない名前」のことを、難しい用語で「メタ構文変数」と呼びます。
★重要★ ?をつければ、変数にnilを代入できる。
実際にnilを使うときの注意点
ここからは、nilを代入できる🙆♂️ 変数を使うときの注意点について話します。
なぜ?をつけて区別するの?
みなさん、疑問に思いませんか?
なんでわざわざ
?をつけて普通の変数と区別するの?
普通の変数に直接nilを代入しちゃえば良くない?↓こんな感じで↓
let hoge: Int = nil //?をつけていないのにnilを代入しているので、もちろんエラー❌
では、なぜ「例2」のように書けないのでしょう?
答えは、「nilも代入できる」というのがなかなか厄介だからです。
nilの厄介な一面
その厄介な点というのは、プログラムの中でnilを直接扱うと、エラーとなり、アプリが落ちてしまうことです。
nilを代入できる🙆♂️ 変数には、常に「もしかしたら中身がnilかもしれない」という可能性があります。
そのような、「もしかしたら中身がnilかもしれない変数」をプログラムでそのまま使ってしまうと、実際に中身がnilだった場合にエラーとなり、アプリが落ちてしまいます。
使っているアプリが突然落ちるなんて、勘弁してほしいですよね。
「nilによるエラー」を防ぐための取り組み
そのようなnilによるエラーを防ぐために、Swiftでは
1. nilを代入できない🙅♂️ 変数 と
2. nilを代入できる🙆♂️ 変数
の2つを同じように扱えないようになっています。
↓具体的にはこんな感じ↓
let hoge: Int = 0 //nilを代入できない🙅♂️
let fuga: Int? = 1 //nilを代入できる🙆♂️
print(hoge + fuga) //nilを代入できない🙅♂️ と nilを代入できる🙆♂️ を同じように扱ったのでエラー❌
「例3」で、hogeはIntとなっているので nilを代入できない🙅♂️ 変数、fugaはInt?のように?がついているので nilを代入できる🙆♂️ 変数です。
print(hoge + fuga)のところでは、nilを代入できない🙅♂️ 変数と nilを代入できる🙆♂️ 変数を同じように扱ってしまっているため、エラーになってしまっているのです。
★重要★ IntとInt?は同じように扱えない。
nilにまつわる「型」の話
では、nilを代入できない🙅♂️ 変数と nilを代入できる🙆♂️ 変数のどこが違うために、同じように扱えないのでしょう?
実は、これら2つの変数の違いには「型」が関係しています。
(ここで言う「型」は、String型やInt型と言うときの「型」と同じようなものだと考えてください。)
このことについて、先ほどの「例3」の事例で説明すると、Intとなっているhogeと、Int?となっているfugaが、同じように見えて実は違う「型」だということです。
ここでひとつ、考えてみてほしいんですが、下の「例4」のように、String型の変数とInt型の変数をまぜこぜにして扱えませんよね。
print("こんにちは" + 3) //String + Intは計算できないのでエラー❌
これと同じように、nilを代入できない🙅♂️ 変数と nilを代入できる🙆♂️ 変数においても、そもそも型が違うのでまぜこぜにして扱えないのです。
ちなみに!
Int?のような「?付きの型」には、「オプショナル型」という名前がついています。
★重要★ IntとInt?は違う「型」
型をそろえる / ?を取る
つまり、「例3」のような計算をするためには、まず「Int?をIntに変換して、型をそろえる作業」が必要なのです。
これもう少し簡単に言うと、「?を取る作業」 となります。
「例3」では、この作業をしなかったのでエラーになったというわけです。
★重要★ nilを代入できる🙆♂️ 変数を使う前に、「?を取る作業」が必要
?の取り方3選
以下では、「?を取る3つの代表的な方法」について、順番に見ていきます。
ひとつめ: !
一つ目は!です。これは簡単に使えますが、力技です。
!の使い方
nilを代入できる🙆♂️ 変数の後ろに!をつけます。
この!によって、変数の中身に関わらず強制的に?が取れます。
(「強制的に」というのがとても重要です。)
let hoge: Int = 0
let fuga: Int? = 1
print(hoge + fuga!) //fugaの"?"を取って計算している。結果は1⭕️
しかし、はじめに力技と言った通り、この方法は万能ではありません。
下の「例5」のように、fugaの中身がnilの場合、!を使うとエラーとなってアプリが落ちてしまいます。
let hoge: Int = 0
let fuga: Int? = nil //例4と例5では、ここが"1"から"nil"に変わった
print(hoge + fuga!) //nilに対して"!"を使えない。よってエラーとなる❌
そのため、この方法は、変数の中身が確実にnilではないと分かっているときしか使えません。
ふたつめ: ??
二つ目は??です。これも、簡単に使えます。
??の使い方
nilを代入できる🙆♂️ 変数の後ろに??をつけ、さらにその後ろに、nilだった場合に代わりに使う値を書きます。
こうすることで、変数の中身がnilだった場合に、その代わりとして??の後ろの値が使われます。
let hoge: Int? = 1
print(hoge ?? 0) //hogeがnilではないので、hogeの値がそのまま出力される。結果は1⭕️
let hoge: Int? = nil
print(hoge ?? 0) //hogeがnilなので、??の後ろの値が出力される。結果は0⭕️
??を使う場合、中身がnilだった場合の「身代わり」となる値が後ろに用意されています。
そのため、!を使ったときのように、エラーとなってアプリが落ちることがありません。
つまり、、??を使う方が、!を使うよりも安全にnilを扱うことができます。
みっつめ: if let
三つ目はif letです。
こちらは少し難しいですが、理解さえすればnilを扱うときの幅が大きく広がります。
if letの使い方
新たに用意した変数に nilを代入できる🙆♂️ 変数を入れてみて、その中身がnilじゃない場合にのみif文の処理を行います。
逆に、もし変数の中身がnilだった場合、if文の処理は行われません。
let hoge: Int? = 1
if let fuga = hoge { //とりあえずfugaにhogeを代入 → 中身がnilじゃなかったらif文の処理を行う
print(fuga)
print("nilじゃなかったよ😊")
}
//結果⭕️
//1
//nilじゃなかったたよ😊
また、elseを追加することで、変数の中身がnilだった場合に行うべき処理も書けます。
let hoge: Int? = nil
if let fuga = hoge {
//ここは中身がnilじゃない場合に実行される (この例では、hogeの中身がnilなので実行されない)
print(fuga)
print("nilじゃなかったよ😊")
} else {
//ここは中身がnilだった場合に実行される
print("nilやんけ😡")
}
//結果⭕️
//nilやんけ😡
このように、if letでは、変数の中身がnilでない場合にのみ行うべき処理を書くことができます。
また、変数の中身がnilだった場合とそうでない場合で、行うべき処理を明確に分けられる点も、if letの特徴だということができます。
ちなみに!
これまでに3つ見てきた「?を取る作業」には、「アンラップ」という名前がついています。
まとめ
-
nilは「空っぽ」を表す。 - 型の名前の後ろに
?をつければ、変数にnilを代入できる。 -
IntとInt?は違う「型」 -
nilを代入できる🙆♂️ 変数を使う前に、「?を取る作業」が必要
おわりに
この記事は、初心者の理解のとっかかりになればと考えて書きました。
そのため、「guard let」や「オプショナルバインディング」など、説明を省いた概念や用語があります。
もし「もっと学びを深めたい」ということでしたら、下の記事を読むのが良いと思います。
この記事では説明できなかった概念や用語なども含めて、とても分かりやすく説明されています。
最後に、誤字や脱字があったり、内容が誤っていたりしたら教えていただけると嬉しいです。