IUOについて誤解していたこと

今回は、僕がSwiftのIUOについて誤解していたことについてまとめます。
同じ勘違いをしている方が他にもいると思うので、学んだことを共有しようと思います。

# 問題（１）
ご存知の通り、IUOは型名に`!`をつけた型です。
```Swift
var text: String!
text = "Hello"
print(text.count)  // 5
```
それでは、次の例ではどうなるでしょうか？
```Swift
var text: String!
text = "Hello"
print(text)  // 何と表示される？
```
`Hello`と表示されると思うかもしれません。
それは不正解で、`Optional("Hello")`と表示されます。

# 問題（２）
ご存知の通り、IUOは型名に`!`をつけた型です。
```Swift
var text: String!
print(text.count)  // クラッシュ
```
上の例では、値をセットする前にアクセスしてしまっているので、クラッシュします。
それでは、次の例ではどうなるでしょうか？
```Swift
var text: String!
print(text)  // どうなる？
```
これも値をセットする前にアクセスしているので、クラッシュすると思うかもしれません。
それは不正解で、`nil`が表示されます。

# そもそもIUOとは？
`String?`と`String!`の違いは何でしょうか？ 今まで僕はこう考えていました。
- `String?` ... 文字列かnilのどちらかが入っているという直和型(Optional)。
- `String!` ... 必ず文字列が入っていると確信しているのでnilにはならない。

しかし、**これは大きな誤解**です。
[Swift.org](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/thebasics/#Implicitly-Unwrapped-Optionals)では、IUOは次のように説明されています。

> You can think of an implicitly unwrapped optional as **giving permission for the optional to be force-unwrapped if needed**.
> 訳：IUOは、**必要であれば強制的にアンラップされることを許可している**と考えるといい。

この一文が全てを物語っているのですが、分かりやすくコードを使って説明しましょう。

# IUOはオプショナル型
そもそも、`String?`も`String!`も同じ**オプショナル型**です。
```Swift
var a: String? = "test"
var b: String! = "test"
print(a)  // Optional("test")
print(b)  // Optional("test")
```
つまり両者のデータ構造には、何の違いもありません。

# `?`と`!`の違いはどこにあるのか
`String?`と`String!`の間に違いが現れるのは、**メンバにアクセスする時**だけです。
```Swift
var a: String? = "test"
var b: String! = "test"
print(a?.count)  // Optional(4)
print(a!.count)  // 4
print(b.count)   // 4
```
`String?`ではメンバにアクセスする時、`?.`と`!.`の2通りの方法がありますよね。`!.`は強制アンラップといって、`a`に値が入ってなければクラッシュさせることで、オプショナルを解除(アンラップ)します。
**この`!.`の`!`を省略できるようにしたものがIUOです**。
これが、IUOが「Implicitly Unwrapped Optional(**暗黙的アンラップ型**)」と名付けられた所以です。

# 必要であればアンラップされる
既に述べたSwift.orgの引用文にある「必要であれば」という言葉も、IUOの挙動を理解する上で重要です。
> You can think of an implicitly unwrapped optional as giving permission for the optional to be force-unwrapped **if needed**.
> 訳：IUOは、**必要であれば**強制的にアンラップされることを許可していると考えるといい。

以下のコードを使って説明します。
```Swift
var a: String! = "hoge"
var b: String? = a  // b=Optional("hoge")  されない
var c: String = a   // c="hoge"  される
func f(s: String) { print(s) }
print(a)  // Optional("hoge")  されない
f(s: a)   // hoge  される
```

`a`の値が使われている2, 3, 5, 6行目をそれぞれ見ていきましょう。
- 2行目、`b`に代入するときはOptionalのままでもいいので、アンラップされません。
- 3行目、`c`に代入するときはOptionalのままではいけないので、暗黙的に強制アンラップされます。
- 5行目、`print`の引数はOptional可なので、アンラップされません。
- 6行目、`f`の引数はOptionalではないので、暗黙的に強制アンラップされます。

この例から、アンラップが必要ない場面では、IUOのアンラップは行われないことが分かります。

# 初期値なしの非オプショナル型
IUOがオプショナルで、暗黙的に強制アンラップしているという事実から、必要もなく**IUOの使うのは避けた方がいい**です。
例えば、イニシャライザで初期化されるようなプロパティは**初期値なしの非オプショナル型**を使うといいです。
```diff_swift
 class Main {
-    let text: String!
+    let text: String

     init(text: String) {
         self.text = text
     }
     
     func main() {
         print(text.count)  // 5
     }
 }
 
 Main(text: "Hello").main()
```
初期値なしの非オプショナル型は、もし初期化漏れなどで、**値が入っていないままアクセスされる危険があるときは、実行する前からエラーを出して教えてくれる**ので便利です。
下の例のように、関数内のローカル変数でも使えます。
```Swift
let num = 123
let r: String
if num.isMultiple(of: 2) {
    r = "偶数"
} else {
    r = "奇数"
}
print(r)  // 奇数
```
もしこのif文の条件網羅や`r`の初期化に漏れがあった場合、コンパイラが実行前エラーで教えてくれます。
IUOを回避する一つのパターンとして覚えておきたいです。