SwiftのOptionalのベストプラクティス

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">それでは、『SwiftのOptionalのベストプラクティス』というタイトルで発表します。 <a href="https://twitter.com/hashtag/swtws?src=hash">#swtws</a> <a href="https://t.co/OJfz2tk4sP">pic.twitter.com/OJfz2tk4sP</a></p>&mdash; koher (@koher) <a href="https://twitter.com/koher/status/853235705996169216">2017年4月15日</a></blockquote>

2 年程前、 Qiita に[『SwiftのOptional型を極める』というタイトルで投稿](http://qiita.com/koher/items/c6f446bad54442a28bf4)をしました。内容は、 `Optional` とは何かという説明と、 `Optional` の使い方を合わせたものでした。

しかし、 Swift 1 だった当時と比べると `Optional` 周りの状況は大きく変わり、特に使い方の部分が適切ではなくなってきました。また、僕のSwift経験値もたまって、新たな知見も蓄積されてきました。

今回の投稿では、 Swift の `Optional` 周りを整理し、いつ・どのように `Optional` を使うべきか、僕の考えるベストプラクティスを紹介します。

全体を通して、[以前投稿したSwiftのエラー4分類（Simple domain error、Recoverable error、Logic failure等）](http://qiita.com/koher/items/a7a12e7e18d2bb7d8c77)の考え方が出て来るので、適宜内容を参照して下さい。

## いつOptionalを使うか

`Optional` を使うのは、主に次の二つの場合です。

- 値が存在しないかもしれないとき
- Simple domain error が発生し得るとき

### 値が存在しないかもしれないとき

たとえば、会員登録時に年齢が任意項目なら、 `User` インスタンスの `age` の値は存在しないかもしれません。値が存在しないことを `nil` で表すために `Optional` を用います。

```swift
struct User {
  var firstName: String
  var lastName: String
  var age: Int? // 値がない場合は `nil`
}
```

### Simple domain errorが発生し得るとき

Simple domain error とは、ハンドリング時にエラーが発生したことさえわかれば良いというエラーです。前提条件が明確で、エラーの詳細な情報が不要な場合に適しています。

Simple domain error のわかりやすい例は、文字列を整数にパースする処理です。 `"123"` という文字列は `123` に変換できますが、 `"xyz"` のような文字列は整数として解釈できないのでパースに失敗します。前提条件が明確で、エラーの原因も明らかです。

そのような失敗を `nil` で表すために `Optional` を使います。エラー処理は Optional binding で行います。

```swift
guard let number = Int(string) else {
  ... // エラー処理
}
// `number` を使う処理
```

値が存在しないのか、 Simple domain error なのかは厳密に区別ができないこともあります。たとえば、配列が空のときに `first` が `nil` なのは、値が存在しないとも、最初の値を取り出そうとして失敗したとも解釈できます。

## どのようにOptionalを使うか

Swift には `Optional` 関連の道具がたくさんあります。それらは便利ではありますが、どのような道具も使い方を間違えるとひどい目にあいます。そういうわけで、次に `Optional` をどのように使うかについて説明します。

### Optionalはできるだけ早く処理する

基本的に、 `Optional` は値を得たらすぐに処理するのが望ましいです。 `Optional` なプロパティはローカル変数に取り出してすぐに、 Simple domain error はエラー発生直後に処理をします。

**⛔ Optionalのまま値を取り回す**

```swift
let foo: Foo? = ...
let bar: Bar? = foo?.bar
guard let baz = bar.map { Baz($0) } else { ... }
// `baz` を使う処理
```

**✅ Optionalをすぐに処理する**

```swift
guard let foo = ... else { ... }
guard let bar = foo.bar else { ... }
guard let baz = Baz(bar) else { ... }
// `baz` を使う処理
```

`nil` は値がないことしか表さないので、どういう経緯で値がnilになったのかを後から知ることができません。 `Optional` のまま値を取り回して後で処理しようとすると、途中のどの段階で `nil` が発生したのかわからず、デバッグする時にやっかいです。

みなさんも、 `Optional` のまま値を取り回して想定外の `nil` を得、その原因箇所を特定するのに苦労したことがあるんじゃないでしょうか。 `Optional` 値を得た直後に処理するようにしておけば、どこで `nil` が発生したのか簡単にわかります。

`Optional` の処理は基本的に `guard let` を使うのが良いでしょう。 `guard` の `else` ブロックでは必ず脱出しないといけないので、 `guard let` だと `if let` と比べてネストしづらいですし、早期脱出のための分岐だと明確になり可読性も高まります。

後続の処理があり `return` や `break` しては困るケースも稀にありますが、大抵のケースでは `guard` で書けるはずです。一見、早期脱出できないように見えても、その部分を関数に切り出した方が適切だということも多いです。

`guard` が使えないと思ったら、一度その関数・メソッドの実装を疑ってみるといいと思います。処理の粒度が大きすぎるのかもしれません。処理をより細かいいくつかの関数に分解できないか考えてみましょう。

**⛔ 一見guardで書けなさそうなのでifで書く**

```swift
let foo: Foo

// ⛔ Optional binding がネストする
if let bar = ... {
  if let baz = ... {
    foo = Foo(baz: baz)
  } else {
    foo = Foo(bar: bar)
  }
} else {
  foo = Foo(42)
}

// `foo` を使う後続処理
```

**✅ 関数に切り出しguardで早期脱出する**

```swift
// ✅ Optional binding がネストしない
func makeFoo(...) -> Foo {
  guard let bar = ... else {
    return Foo(42)
  }
  guard let baz = ... else {
    return Foo(bar: bar)
  }
  return Foo(baz: baz)
}

let foo: Foo = makeFoo(...)

// `foo` を使う後続処理
```

処理を関数に切り出すとテスタビリティも上がります。このように複雑な条件分岐を伴う処理は、たとえ数行であっても関数に切り出して単体テストを書いた方が、結果的に時間を削減できることが多いです。

そういうわけで、僕は `if let` よりも `guard let` を好んで使っているんですが、一つ問題があります。シャドーイングができないことです。

```swift
let foo: Int? = 42

// これはできる
if let foo = foo { ... }

// けど、これはできない
guard let foo = foo else { return }
```

`if let foo = foo` のように、元の変数と同じ名前で Optional binding するケースは多いと思います。 `guard let` の場合、これができません。 `if let` と違ってスコープが切られないためです。

これについては日常的に不便を感じているので、もし良い代替案を知っている方がいれば教えてもらえるとうれしいです。

なお、 `if let` を使うケースとして、 `Optional` 値が `nil` でないときの早期脱出があります。

```swift
if let _ = foo {
  return
}
// `foo` が `nil` のときの処理
```

### !はunwrap時に絶対にnilにならないときだけ使う

Swift には `foo!`, `Foo!`, `try!`, `as!` などいくつかの!があります。すべてに共通しているのは、絶対にその処理が失敗しないときに使うべきものだということです。

ここでは、 `Optional` と関係のある次の二つについて説明します。

- Forced unwrapping (`foo!`)
- Implicitly unwrapped optional (`Foo!`)

#### Forced unwrapping (foo!)

`foo!` と書くと、 `foo` が `nil` だった場合にプログラムを停止させられます。そのようなエラーは Logic failure に分類されます。

Logic failure は、実行時にハンドリングして解決するエラーではありません。 Logic failure が起こったということはプログラム自体に何らかの問題があることを意味し、プログラムの修正によって解決を図ります。

裏を返せば、 Logic failure でクラッシュするのはプログラムのバグなので、 `nil` になる可能性がある値に対して `!` を使ってはならないということです。 `!` は絶対に `nil` にならないことがわかっている値にのみ利用すべきです。

では、絶対に `nil` にならないとはどのようなケースでしょうか。絶対に `nil` にならないのであれば、最初から `Optional` にしなければ良いのではないでしょうか。

残念ながら型は万能ではありません。型の上では `Optional` になるけれども、ロジック上、絶対に `nil` にならないケースが存在します。そういう場合に `Optional binding` で分岐するのは無駄です。!で非 `Optional` 値に変換します。

僕がいつも挙げる例なので聞き飽きた人もいるかもしれませんが、たとえば、テキストフィールドに入力された文字列を整数に変換する処理を考えて下さい。 UI の制約で数値しか入力できないとすると、この処理は絶対に失敗しません。

**⛔ 絶対にnilでないケースで条件分岐する**

```swift
// `numberField` には数値しか入力できない
guard let number = Int(numberField.text) else {
  fatalError("Never reaches here.") // 無駄な処理
}
// `number` を使う処理
```

**✅ 絶対にnilにならないなら!で非Optional値に変換する**

```swift
// `numberField` には数値しか入力できない
let number = Int(numberField.text)!
// `number` を使う処理
```

なお、 Logic failure はコードの問題だけを意味するわけではありません。たとえば、アプリにバンドルされ必ず存在するはずの設定ファイルが存在しないというのも Logic failure です。

必ず存在するはずの設定ファイルが存在しないなら、設定ファイルを作成するのがその Logic failur eに対する正しい対処になります。ファイルが存在しなくてもクラッシュしないようにコードを修正するのではありません。

使い捨てのスクリプト等で!を使いたいケースはどう考えれば良いでしょうか。 CUI で文字列を入力させ、整数に変換する場合を考えてみましょう。 GUI のように入力可能な文字を制御することもできません。しかし、使い捨てのコードのためにわざわざ失敗ケースを考えたくありません。

入力された文字列は数値に変換できるとは限らないので、パースの結果が「絶対に `nil` にならない」とは言い切れません。でも、こんな場合にも!は使いたいはずです。

これは僕の解釈ですが、 Logic failure の考え方を拡張すればこのようなケースにも適用できます。

この使い捨てスクリプトには UI 上の制約はありませんが、数値しか入力してはいけません。一般的なアプリでは、バグらないように正しく振る舞う責務はユーザーにはありません。しかし、このスクリプトではその責務をユーザーに負わせているわけです。

この場合、 `!` でクラッシュしたなら、責務を果たさず前提条件を破ったのはユーザーです。つまり、ユーザーの Logic が失敗したのであってコードのバグではありません。ユーザーがバグってたのです。

バグってるのはユーザーなら、この Logic failure の正しい対処は、ユーザーが行動を改めることです。ユーザーが正しく振る舞えば、パース結果は絶対に `nil` にはなりません。

このように解釈することで、「絶対に `nil` にならない」ときにだけ!を使うという単一の基準で考えることができます。「絶対に `nil` にならない」ことを担保するのが、コードなのか、ファイルや DB を含むシステム全体なのか、ユーザーなのかが変わるだけです。

#### Implicitly unwrapped optional (Foo!)

`Foo!` も基本的には `foo!` と同じように考えれば OK です。つまり、 unwrap 時に絶対に `nil` にならないときだけ使うということです。

これは、 Implicitly unwrapped optional (以下 IUO )の挙動を考えれば明白です。 `Foo!` は基本的に、 `Foo?` 型の値に対して常に `!` を付けてアクセスするのと同じ挙動をします。

```swift
// これ↓と
let foo: Foo! = ...
let bar = foo.bar
baz(foo)

// これ↓は同じ
let foo: Foo? = ...
let bar = foo!.bar
baz(foo!)
```

Forced unwrapping の失敗は Logic failure で、絶対に `nil` にならないときにだけ使えるものでした。 IUO では自動的にそれが実行されるということは、 unwrap される箇所では絶対に `nil` になっていてはいけないことを意味します。

IUO の典型的なユースケースは IBOutlet です。 Swift の言語仕様上、 `Optional` でないプロパティはイニシャライザで初期化されなければなりません。しかし、 IBOutlet はその仕組みの都合上、イニシャライザで初期化を実行することができません。

現実的には、 `viewDidLoad` が呼ばれるときには IBOutlet なプロパティは初期化されており、それらを利用するときに `nil` になっていることはありません。つまり、ロジック上「絶対に `nil` にならない」わけです。

もしそのプロパティが普通の `Optional` だと、「絶対に `nil` にならない」ことがわかっているので、すべての箇所で `!` を付けて Forced unwrapping しなければなりません。これは無駄です。そんなときには型レベルで `!` を付けられれば便利です。

もし IUO に対して `nil` チェックをしたり、 Optional binding を書いたりしていたら、設計がまずいのではないかと疑ってみましょう。その値は IUO ではなく `Optional` であるべきかもしれません。

次のようなケースはどうでしょうか。 `viewDidAppear` から `viewWillDisappear` の間では「絶対に `nil` にならない」ことが保証されており 99% はそこからアクセスされる、けれども、一箇所だけ `viewWillAppear` からアクセスする必要がある。

そのようなプロパティは IUO にして、 `viewWillAppear` で IUO に対して `nil` チェックしたり Optional binding すべきでしょうか。それとも、普通の `Optional` にして 99% のアクセスに!を付けるべきでしょうか。

僕は、委譲が答えになるんじゃないかと考えています。問題は、 `ViewController` のライフサイクルとそのプロパティのライフサイクルのずれです。そのプロパティを使う処理を別の型に委譲してしまい、その型では非 `Optional` 値として保持すれば良いのです。

**⛔ IUOをOptional bindingして分岐する**

```swift
class ViewController: UIViewController {
  // 99% は `nil` でないので IUO にする
  var foo: Foo!
  ...

  override func viewWillAppear(_ animated: Bool) {
    ...
    // ⛔ IUO を Optional binding
    guard let foo = self.foo { return }
    // `foo` を使う処理
  }

  override func viewDidAppear(_ animated: Bool) {
    ...
    foo = Foo(...)
  }

  override func viewWillDisappear(_ animated: Bool) {
    ... // `foo` が開放されるかもしれない処理
  }

  func useFoo(...) {
    // `foo` を使う処理
    // ✅ Forced unwrappingでなくて良い
    foo.bar(...)
    foo.baz(...)
    foo.qux(...)
    ...
  }
}
```

**⛔ たった一箇所のためにForced unwrappingだらけにする**

```swift
class ViewController: UIViewController {
  // IUO を Optional binding したくないので Optional にする
  var foo: Foo?
  ...

  override func viewWillAppear(_ animated: Bool) {
    ...
    // ✅ 普通の Optional の binding
    guard let foo = self.foo { return }
    // `foo` を使う処理
  }

  override func viewDidAppear(_ animated: Bool) {
    ...
    foo = Foo(...)
  }

  override func viewWillDisappear(_ animated: Bool) {
    ... // `foo` が開放されるかもしれない処理
  }

  func useFoo(...) {
    // `foo` を使う処理
    // ⛔ たった一箇所のために Forced unwrapping だらけ
    foo!.bar(...)
    foo!.baz(...)
    foo!.qux(...)
    ...
  }
}
```

**✅ スコープを切って委譲する**

```swift
class ViewController: UIViewController {
  struct FooUser {
    let foo: Foo

    func useFoo(...) {
      // `foo` を使う処理
      // ✅ Forced unwrappingでなくて良い
      foo.bar(...)
      foo.baz(...)
      foo.qux(...)
      ...
    }
  }

  var fooUser: FooUser?
  ...

  override func viewWillAppear(_ animated: Bool) {
    ...
    // ✅ 普通の Optional の binding
    guard let foo = self.fooUser?.foo { return }
    // `foo` を使う処理
  }

  override func viewDidAppear(_ animated: Bool) {
    ...
    fooUser = FooUser(foo: Foo(...))
  }

  override func viewWillDisappear(_ animated: Bool) {
    ... // `fooUser` が開放されるかもしれない処理
  }

  func useFoo(...) {
    // ✅ ここでは絶対に `nil` でないので Forced unwrapping
    fooUser!.useFoo(...)
  }
}
```

### 複数のOptionalをまとめて処理したくなったら要注意

Swift には便利な構文が用意されているので、複数の `Optional` をまとめて処理できます。しかし、まとめて処理するということは `nil` の発生源をわかりづらくしてしまうということでもあります。

複数の `Optional` は、独立して並列に現れるケースと、直列した一連の処理の中で現れることがあります。

**🤔 A: 並列な複数のOptionalをまとめて処理する**

```swift
// 🤔 `a` と `b` のどちらが `nil` かわからない
guard let a = ...,
      let b = ... else { ... }
let sum = a + b
```

**🤔 B: 直列な複数のOptionalをまとめて処理する (1)**

```swift
// 🤔 `array` と `first` のどちらが `nil` かわからない
guard let foo = self.array?.first?.foo else { ... }
// `foo` を使うコード
```

**🤔 C: 直列な複数のOptionalをまとめて処理する (2)**

```swift
// 🤔 `text` と `Int` のどちらが `nil` かわからない
guard let text = input.text,
      let number = Int(text) else { ... }
// `number` を使うコード
```

これらは一概に悪いとは言えません。 `nil` の発生源はわかりづらくなるのでデバッガビリティは低下しますが、コードはすっきりして書きやすいです。個別に処理をすることもできますが、やや冗長に感じられます。

**🤔 A': 並列な複数のOptionalを個別に処理する**

```swift
// 🤔 `guard` を 2 回書かないといけなくてすっきりしない
guard let a = ... else { ... }
guard let b = ... else { ... }
let sum = a + b
```

**🤔 B': 直列な複数のOptionalを個別に処理する (1)**

```swift
// 🤔 元々一つの式だったのが二つに分かれてしまった
guard let array = self.array else { ... }
guard let foo = array.first?.foo else { ... }
// `foo` を使うコード
```

**🤔 C': 直列な複数のOptionalを個別に処理する (2)**

```swift
// 🤔 `guard` を 2 回書かないといけなくてすっきりしない
guard let text = input.text else { ... }
guard let number = Int(text) else { ... }
// `number` を使うコード
```

可読性はどちらも大きく変わりませんし、どちらを採用すべきかはケース・バイ・ケースです。ただし、まとめて処理される `Optional` の数が多くなるほどデバッガビリティを損ねるので注意しましょう。

複数の `Optional` をまとめて処理するのは必ずしも悪いことではありません。しかし、複数の `Optional` をまとめて処理したくなったら、本当にそれが良いのか、一度立ち止まって考えてみましょう。

### map/flatMapはプロパティか関数に渡すときに使う

`map` や `flatMap` を使えば、 `?.` では記述できないケースにも対応することができます。これは Swift 1 の頃には便利でした。

```swift
// `array.fisrt` の結果を引数として渡したい
array.first.map { Foo($0) }
```

しかし、 `Optional` の `map` や `flatMap` を使う機会は減りました。当時は言語サポートのある唯一のエラー処理手段が `Optional` でした。そのため、 Simple domain error も Recoverable error も `nil` で表していました。

Recoverable error は、発生の前提条件が明確でなく様々な原因で発生し、回復処理を記述させたいようなエラーです。ファイル入出力やネットワークのエラーが典型的な例です。

Simple domain error と違って、 Recoverable error は発生後すぐ個別に処理をするのではなく、合成してまとめて処理したり、伝播させたりしたいことが多くあります。

`nil` 発生源がわかりづらいという弊害があっても、 Recoverable error のために、当時は `Optional` のまま値を取り回したい局面がありました。しかし、今は違います。 Recoverable error には `throws` を使い、 `Optional` は使いません。

`map`/`flatMap` は、 `Optional` のまま値を取り回すには便利ですが、 `Optional` をすぐ処理するのであれば可読性を損ねるだけです。

**⛔ Optional binding前にmapで変換する**

```swift
// ⛔ 一見して何をしているのかわかりづらい
guard let foo = dictionary["a"].map { Foo($0) } else { ... }
// `foo` を使う処理
```

**✅ Optional binding後にunwrapされた値を変換する**

```swift
// ✅ 処理の内容が明確で可読性が高い
guard let a = dictionary["a"] else { ... }
let foo = Foo(a)
// `foo` を使う処理
```

**⛔ 一連の処理の中で発生する複数のOptionalを合成してからOptional bindingする**

```swift
// ⛔ 一見して何をしているのかわかりづらい
guard let number = self.string.flatMap { Int($0) } else { ... }
// `number` を使うコード
```

**✅ 一連の処理の中で発生する複数のOptionalを個別にOptional bindingする**

```swift
// ✅ 処理の内容が明確で可読性が高い
guard let string = self.string,
      let number = Int(string) else { ... }
// `number` を使うコード
```

複数の `Optional` を一度に binding するのは、 Swift の構文の中で Haskell の do 記法に最も近いものです。 Haskell ですらモナドを快適に扱うのに do 記法を必要としています。 `flatMap` だけでモナディックなコードを書こうとするのは無謀です。

`Optional` に対して `flatMap` を書きたくなったら、 Optional binding で代替できないか一度考えてみましょう。

`map` や `flatMap` を使う機会があるとすると、戻り値である `Optional` 値をそのまま利用したいときです。主に次の二つのケースが考えられます。

- `Optional` なプロパティに代入する
- `Optional` な引数を持つ関数・メソッドに渡す

どちらの場合も注意が必要です。一歩間違えれば `Optional` をすぐに処理せず、そのまま値を取り回すことにつながりかねません。

何らかの処理の失敗を表すために、 `Optional` なプロパティで `nil` を保持するのは得策ではありません。それは、エラーを `Optional` のまま取り回していることになります。 `Optional` なプロパティは、値がないことを表すために使いましょう。

`map`/`flatMap` の結果をそのままプロパティに代入したいケースとして、 setter 的なメソッドの引数で受けた `Optional` 値をそのままプロパティに保持するのではなく、変換してからプロパティで保持したいケースが考えられます。

**⛔ Optional値の変換のためにわざわざ分岐する**

```swift
init(foo: Foo? = nil) {
  // ⛔ コードが複雑になる
  if let foo = foo {
    self.bar = Bar(foo)
  } else {
    self.bar = nil
  }
}
```

**✅ mapを使ってOptionalのまま値を変換する**

```swift
init(foo: Foo? = nil) {
  // ✅ シンプルに書ける
  self.bar = foo.map { Bar($0) }
}
```

関数やメソッドの引数に `Optional` 値をとる場合も、 `Optional` のまま値を取り回すことになっていないか要注意です。引数に渡す前に `Optional` を処理する実装の方が良いかもしれません。 `Optional` を引数にとる設計が適切か再度考えてみましょう。

引数で `Optional` 受けたいよくあるケースは、省略可能な引数を作る場合だと思います。しかし、このような引数に `map`/`flatMap` の結果をそのまま渡すことは稀だと思います。

```swift
class func animate(withDuration duration: TimeInterval,
        animations: @escaping () -> Void,
        completion: ((Bool) -> Void)? = nil)
```

`Optional` をそのまま引数に渡す代表的な例が `??` です（ Swift の演算子は関数です）。↓の例では `foo?.bar` で `Optional` のまま値を取り回し、それをそのまま `??` に渡しています。

```swift
let bar = foo?.bar ?? Bar(42)
```

`bar` が `foo` のメンバならシンプルに `?.` で書けますが、 unwrap した値を関数やメソッドの引数に渡したい場合はそうはいきません。そのような場合には `?.` の代わりに `map` を使えます。分岐すると複雑なコードも、 `map`/`flatMap` ですっきり書けます。

**⛔ デフォルト値のために条件分岐書く**

```swift
// ⛔ コードが複雑になる
let bar: Bar
if let foo = foo {
  bar = Bar(foo)
} else {
  bar = Bar(42)
}
```

**✅ mapの結果を??に渡す**

```swift
// ✅ シンプルに書ける
let bar = foo.map { Bar(foo: $0) } ?? Bar(42)
```

ただし、 `Optional` の `map` や `flatMap` は可読性を損ねるおそれがあります。クロージャ式で複雑なことはしない、他の式と連結しないなど、最小限の使用に留めるのが望ましいと思います。

個人的には、↓のような構文があったらいいのになぁと考えています。

```swift
// `map` の代わりにこんな風に書けたらうれしい
let bar = Bar(foo: foo?) ?? Bar(42)
```

## まとめ

### いつOptionalを使うか

- 値が存在しないかもしれないとき
- Simple domain error が発生し得るとき

### どのようにOptionalを使うか

- `Optional` はできるだけ早く処理する
- `!` は unwrap 時に絶対に `nil` にならないときだけ使う
- 複数の `Optional` をまとめて処理したくなったら要注意
- `map`/`flatMap`は戻り値をプロパティか関数に渡すときに使う