[Swift] 例外をガンガン投げよう！

# じょ
Objective-Cおじさんの中には例外はコストが高いから可能な限り投げたくないと考える人もいるでしょう。
でも**Swiftならガンガン投げてもいいんです！**
Objective-Cの例外はsetjmp/longjmpで実装されているため結構なコストがかかりましたが、Swiftの例外はほとんどコストがかからないように設計されています。
なので**Swiftならガンガン投げてもいいんです！**

# 問題
例外をガンガン投げることにしたんですが、Swiftでは別の問題が生じます。
それは**Errorを定義するのが面倒くさい！** ことです。

このエントリではこの面倒くさいをなくします。
具体的にはprotocolに準拠するだけで`Error`を定義することなく簡単に例外を使えるようにしちゃいます。

## どうすんの？

### ぷろとこる
まず準拠すべきプロトコルを定義します。
```swift
protocol ErrorGeneratable {}
```
以上で完成です。
プロパティもメソッドもありませんが気にしてはだめです。
### えらー型
このプロトコルだけでは当然何もできませんので生成される`Error`を定義します。
```swift
struct GeneratedError<T: ErrorGeneratable>: Error {
    let host: T
    let id: Int
    let message: String

    let file: String
    let line: Int
}
extension GeneratedError: CustomStringConvertible {
    var description: String {
"""
GeneratedError {
    host:     \(host),
    id:       \(id),
    message:  "\(message)",
    position: \(file)#\(line)
}
"""
    }
}
```
hostは例外を生成したインスタンスが入るようになります。
他はなんとなくわかるでしょう。

### ぷろとこる えくすてんしょん
では実際に`ErrorGeneratable`プロトコルが準拠したタイプが`GeneratedError`を生成するメソッドを定義します。
これはprotocol extensionで実装します。
```swift
extension ErrorGeneratable {
    func generateError(_ id: Int, _ message: String, file: String = #file, line: Int = #line) -> GeneratedError<Self> {
        .init(host: self, id: id, message: message, file: file, line: line)
    }
}
```

メソッドをprotocol extensionで実装したのは、デフォルト引数として`#file`と`#line`を使いたいがためでした。
Swift、面倒くさいね。

----
以上で完成です。
わー。ぱちぱちぱち。

## 使ってみる

### 準拠
```swift
struct Hoge: ErrorGeneratable {
    func hoge(_ f: Bool) throws {
        if f {
            throw generateError(0, "ほげー")
        }
    }
}

struct Fuga: ErrorGeneratable {
    func fuga0(_ f: Bool) throws {
        if f {
            throw generateError(0, "ふがー")
        }
    }
    func fuga1() throws {
        throw generateError(1, "ふがーふがー")
    }
}
```
興味深いstructができました。

### きゃっち
キャッチしてみましょう。
```swift
do {
    try Hoge().hoge(true)
}
catch {
   print(error)
}

// prints
// GeneratedError {
//    host:      Hoge(),
//    id:        0,
//    message:   "ほげー",
//    positiion: main.swift#32
// }
```
キャッチした例外がどこで生成されたかわかって便利そうです。

### きゃっち（分岐）
例外の種類によって処理を分岐するのは当然の事です。
ではやってみましょう。

```swift
do {
    try Hoge().hoge(false)
    try Fuga().fuga0(true)
    try Fuga().fuga1()
}
catch is GeneratedError<Hoge> {
    print("ほげー")
}
catch let error as GeneratedError<Fuga> {
    switch error.id {
    case 0: print("Fuga id 0\n", error)
    case 1: print("Fuga id 1\n", error)
    default: print("Unknown\n", error)
    }
}
```
`ErrorGeneratable`の生成する`GeneratedError`は型パラメータの違いによって識別可能です。
このため例外による分岐は単純な方法で実現可能です。

またFugaのように複数の種類の例外を生成したとしても`GeneratedError`に`id`プロパティがあるため一応分岐可能です。
この部分はもうちょっとうまい事やっても罰は当たらないかもしれません。
たとえば`ErrorGeneratable`プロトコルにアソあばばっばっっばばｂ（後述）

## まとめ
`ErrorGeneratable`プロトコルと導入する事でいちいち`Error`型を定義する必要なく例外を利用出来るようになります。
また通常の汎用的な`Error`型ではなく型パラメータにより例外を識別可能でちょっとしたstruct/class
では問題なく利用出来ます。

ということで**ガンガン投げましょう！**

## おまけ
### アソあばばっばっっばばｂ？
例外の識別にInt型のidを使うのはやっぱりちょっと不格好です。
これは`ErrorGeneratable`プロトコルにassociatedtypeを導入する事で解決出来ます。

まず`ErrorGenaratable`プロトコルに`IDType`というassociatedtypeを導入します。

```swift
protocol ErrorGenaratable {
    associatedtype IDType
}
```
ついでGeneratedErrorのidをErrorGenaratable.IDTypeに変更します。

```swift
struct GeneratedError<T: ErrorGenaratable>: Error {
    
    let host: T
    let id: T.IDType
    let message: String
    
    let file: String
    let line: Int
    
    init(_ host: T, id: T.IDType, message: String, file: String, line: Int) {
        self.host = host
        self.id = id
        self.message = message
        
        self.file = file
        self.line = line
    }
}
```
プロトコルエクステンションもそれに合わせて変更します。
```swift
extension ErrorGenaratable {
    
    func generateError(_ id: Self.IDType, _ message: String, file: String = #file, line: Int = #line) -> GeneratedError<Self> {
        .init(self, id: id, message: message, file: file, line: line)
    }
}
```
準拠するタイプはtypealiasで`IDType`を指定します。

```swift
struct Hoge: ErrorGenaratable {
    typealias IDType = Int
    func hoge() throws {
        throw generateError(0, "")
    }
}
struct Fuga: ErrorGenaratable {
    enum ErrorID {
        case foo
        case bar
    }
    typealias IDType = ErrorID
    func fuga0() throws {
        throw generateError(.foo, "")
    }
    func fuga1() throws {
        throw generateError(.bar, "")
    }
}
```
Hogeに関しては`id`で識別する場面がありませんので`Int`としています。
Fugaに関しては新たにenum `ErrorID`を導入してこれを`IDType`としました。
利用コードは次のようになります。
```swift
do {
    try Fuga().fuga0()
    try Hoge().hoge()
    try Fuga().fuga1()
}
catch is GeneratedError<Hoge> {
    print("ほげー")
}
catch let error as GeneratedError<Fuga> {
    switch error.id {
    case .foo: print("Fuga id foo\n", error)
    case .bar: print("Fuga id bar\n", error)
    }
}
```
これできれいに分岐することができるようになりました。

### だが待ってほしい
このエントリの解決する問題とは何だったでしょう？
それは... **Errorを定義するのが面倒くさい！** です。

でも、`Error`の識別にenum作っちゃいましたね....
それするぐらいなら
```swift
enum ErrorID: Error {
    case foo
    case bar
}
```
でよくね？

しかも例外投げるところが
```swift
struct Fuga: ErrorGenaratable {
    enum ErrorID: Error {
        case foo
        case bar
    }

    func fuga0() throws {
        throw ErrorID.foo
    }
    func fuga1() throws {
        throw ErrorID.bar
    }
}
```
でよくね？

さらに分岐は
```swift
do {
    try Fuga().fuga0()
    try Fuga().fuga1()
}
catch Fuga.ErrorID.foo {
    print("Fuga foo\n")
}
catch Fuga.ErrorID.bar {
    print("Fuga bar\n")
}
```
でよくね？

`ErrorGenaratable.IDType`意味なくね？