[Swift][Optional] アンラップしているはずなのに、アンラップされない



# この記事について

この記事は、以前書いた[「[Swift] Optional型についてのまとめ」](http://qiita.com/cotrpepe/items/e30c7442733b93adf46a)の補足記事です。


# 開発環境
* Xcode 6 Beta6 (Playground)
* Mac OS X 10.9.3


# Optional Chaining でアンラップしているはずなのに、アンラップされない

以下のコードを見てください。

Optional Chaining（? を使ってアンラップする方法）でアンラップしているはずなのに、アンラップされていないように見えます。

```swift:
class Dog {}

var wrapped: Dog?  = Dog() // Optional 型
var unwrapped: Dog = Dog() // 非 Optional 型

//
// 非 Optional 型に Optional 型は代入できない
//
unwrapped = wrapped // -> error: value of optional type 'Dog?' not unwrapped...

//
// Forced Unwrapping でアンラップすると、正常に代入できる
//
unwrapped = wrapped!

//
// しかし、Optional Chaining でアンラップすると、代入できない
//
unwrapped = wrapped? // -> error: value of optional type 'Dog?' not unwrapped...
```

Optional 型の変数を、非 Optional 型の変数に代入する際の挙動が、Forced Unwrapping（! を使う方法）と Optional Chaining（? を使う方法）で異なっています。

* Forced Unwrapping（! を使う方法）でアンラップする場合 -> 正常に代入できる

* Optional Chaining（? を使う方法）でアンラップする場合 -> エラーが出る（アンラップしていない、というエラーが出る）

Optional Chaining でアンラップしているはずなのに、「アンラップされていない」というエラーが出てしまいます。なぜでしょうか？

これは、

* アンラップした際の戻り値が、Forced Unwrapping と Optional Chaining で異なる

ためです。以降、詳しく見ていきます。


# Optional Chaining は、戻り値をラップしてから返す

筆者は、Optional Chaining について、

* 一度アンラップしてメソッド等を呼び出したあとに、戻り値を再度ラップし直してから、返している

というイメージで捉えています。

以下の Dog クラスを例に、具体的に見ていきます。

```swift:
class Dog {
    func bark() -> String {
        return "Wan!"
    }
}

var wrapped: Dog? = Dog() // Optional 型
```


# Forced Unwrapping（! を使う方法）の場合、戻り値は、素の型

## アンラップ時の内部処理のイメージ その１

```swift:
println(wrapped!) // -> __lldb_expr_11.Dog（= Dog 型）
```

上記のコードのアンラップ時の内部処理は、以下のようになっているものと推測されます。

1. wrapped をアンラップする
2. アンラップした値を「そのまま」返す

## アンラップ時の内部処理のイメージ その２

```swift:
println(wrapped!.bark()) // -> Wan!（= 非 Optionnal の String 型）
```

1. wrapped をアンラップする
2. bark() を呼び出す
3. bark() の戻り値（非 Optional の String 型）を「そのまま」返す


# Optional Chaining（? を使う方法）の場合、戻り値は、Optional 型

## アンラップ時の内部処理のイメージ　その１

```swift:
println(wrapped?) // -> Optional(__lldb_expr_11.Dog) （= Optional の Dog 型）
```

1. wrapped をアンラップする
2. 再度、wrapped を「ラップし直してから」返す

## アンラップ時の内部処理のイメージ　その２

```swift:
println(wrapped?.bark()) // -> Optional("Wan!") （= Optional の String 型）
```

1. wrapped をアンラップする
2. bark() を呼び出す
3. bark() の戻り値（非 Optional の String 型）を「ラップしてから」返す。

上記のように、Optional Chaining の場合は、最終的な戻り値を Optional 型に「ラップしてから」返しています。

これは、Optional Chaining が、nil を返す可能性があるためです（詳細は、[以前書いた記事](http://qiita.com/cotrpepe/items/e30c7442733b93adf46a)を参照してください）。nil を許容するためには、戻り値は Optional 型でなければいけません。


## 補足（元々の戻り値が Optional 型だった場合、戻り値は Optional 型のまま）

元々の戻り値が Optional 型だった場合は、Optional Chaining の戻り値も、Optional 型のままになります。さらに、ラップするようなことはしません。

### アンラップ時の内部処理のイメージ

```swift:
//
// Dog 型に wrappedBark() を追加
//
extension Dog {
    func wrappedBark() -> String? { // Optional の String 型を返す
        return "Wrapped Wan!"
    }
}

var wrapped: Dog? = Dog()       // Optional 型
println(wrapped?.wrappedBark()) // -> Optional("Wrapped Wan!") （= Optional の String 型）
```

1. wrapped をアンラップする
2. wrappedBark() を呼び出す
3. wrappedBark() の戻り値（Optional の String 型）を「ラップせずに、そのまま」返す。


注）
Optional 型をさらに Optional 型でラップした場合は、以下のような出力になります。

```swift:
var i: Int?? = 1 // Optional 型の Optional 型
println(i)       // -> Optional(Optional(1)) 
```


# Optional Chaining と Optional Binding を組み合わせた場合

## アンラップ時の内部処理のイメージ 

```swift:
if var unwrapped = wrapped?.bark() {
    println(unwrapped) // -> Wan! （= 非 Optional の String 型）
}
```

[Optional Chaining] 1. wrapped をアンラップする
[Optional Chaining] 2. bark() を呼び出す
[Optional Chaining] 3. bark() の戻り値（非 Optional の String 型）を「ラップしてから」返す。
[Optional Binding ] 4. 上記 3. の値をアンラップしてから、unwrapped に代入する


# Forced Unwrapping と Optional Binding を組み合わせた場合

## アンラップ時の内部処理のイメージ 

```swift:
if var unwrapped = wrapped!.bark() { // -> error: bound value in a conditional binding must be of Optional type
}
```

Optional Binding では、代入する側の値（ここでは、wrapped!.bark()）は Optional 型でなければなりません。上記のコードでは、wrapped!.bark() の戻り値が、非 Optional 型であるため、エラーが発生します。

[Forced Unwrapping] 1. wrapped をアンラップする
[Forced Unwrapping] 2. bark() を呼び出す
[Forced Unwrapping] 3. bark() の戻り値（非 Optional の String 型）を「そのまま」返す
[Optional Chaining] 4. 上記 3. の値をアンラップすることができないため、エラーが発生する


## Implicitly Unwrapped Optional 型の場合、戻り値は、素の値

Implicitly Unwrapped Optional 型についても、見ていきます。

```swift:
var iwrapped: Dog! = Dog() // Implicitly Unwrapped Optional 型
```

## アンラップ時の内部処理のイメージ　その１

```swift:
println(iwrapped) // -> __lldb_expr_11.Dog （= Dog 型）
```

1. iwrapped を「暗黙的に」アンラップする
2. アンラップした値を「そのまま」返す

## アンラップ時の内部処理のイメージ　その２

```swift:
println(iwrapped.bark()) // -> Wan! （= 非 Optional の String 型）
```

1. iwrapped を「暗黙的に」アンラップする
2. bark() を呼び出す
3. bark() の戻り値（非 Optional の String 型）を「そのまま」返す


# 注意

上記の一連の内部処理のイメージは、筆者の推測によるものです。コンパイラの実装がどうなっているのかは不明です。

ただ、上記のように捉えておけば、アンラップ時の戻り値について、理解しやすいのではないかと思います。


# まとめ

他のアンラップ方法と異なり、Optional Chaining （? を使う方法）は、一度アンラップしたあとに、最終的な戻り値を Optional 型にラップしてから返します。

Optional Chaining を使う際は、注意してください。


|アンラップの方法                   |例             |戻り値|
|:-------------------------------|:--------------|:----|
|Forced Unwrapping               |wrapped!.bark()|素の型|
|Implicitly Unwrapped Optional 型|wrapped.bark() |素の型|
|Optional Chaining               |wrapped?.bark()|Optional 型（最終的な戻り値を Optional 型にラップする）|


# 参考文献

以下の 1. のブログ記事（「疑問」の項目）が、今回の記事を書くきっかけになりました。ありがとうございました。

1. [NTEC - 【Swift入門】Optional型 (?, !) をまとめてみた](http://ntech.hatenadiary.com/entry/2014/07/10/135444)

2. [The Swift Programming Language]
(https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-XID_0)