【Swift】do-catch文でエラー処理を行う（その２）

この記事は、[【Swift】do-catch文でエラー処理を行う（その１）](https://qiita.com/onishiApp/items/cba5e6c7361163d8931e)の続きです。

前回の記事では、do-catch文の基本的な実装方法と
`throwsキーワード`, `rethrowsキーワード`に関して記載しました。
これらのキーワードを聞いて全くピンと来ない方はぜひご覧ください！

では続きになります。

###tryキーワード

tryキーワードは、**エラーを発生させる可能性のある処理を実行する際に追加します。**
つまり、throwsキーワードが指定された処理を呼び出す場合です。

それらの処理の呼び出し前にtryキーワードを追加して、
`try 関数名(引数)`のように記述します。

次のサンプルコードでは、
Int型のMax値の半分以下の値が引数に渡されたら、その値を２倍にして返し、
Max値の半分以上の値だった場合はエラー処理を行っています。

guard文の中にreturnがないではないか！と思うかもしれませんが、
**必ずしもreturnが必要なわけではなくthrowでも大丈夫だそうです！**

値を返すのではなく、エラーを返しているイメージですかね。

今回は、100を渡すパターンと、Int型のMax値を渡しています。
後者はもちろんguard文でひっかかりますのでエラー処理になります。

```swift

enum SomeError: Error {
    case overInt
}

func double(value: Int) throws -> Int {
    guard value <= Int.max / 2 else {
        throw SomeError.overInt
    }
    
    return value * 2
}

do {
    let value1 = try double(value: 100)
    print(value1)
    let value2 = try double(value: Int.max)
    print(value2)
} catch {
    print("Error: \(error)")
}

実行結果
200
Error: overInt
```

###try！キーワード

出たよ`！`と思ってしまうくらい「！」は使われますよね・・・。

try!以外での`！キーワード`の使い道としては、
Optional\<Wrapped>型の強制アンラップとかが有名ですかね。

try!キーワードも結構強気なタイプです(笑)

**throwsキーワードを指定されている処理はtryキーワードを追加する**と先ほど書きました。

しかし、特定の場面では絶対にエラーが発生しないと分かっていて、
わざわざエラー処理を記述したくないケースもあると思います。

その場合に使えるのが`try！キーワード`です。
**tryの代わりにtry!にするとエラーを無視できます。**

また、try!キーワードはエラーを無視するので、
do節内やthrowsキーワードが指定された処理の内部でなくても使用できます。

先ほどのサンプルコードの内容を一部変更しました。
value1は上手くいっていますが、value2で実行時エラーが発生しています。

```swift

enum SomeError: Error {
    case overInt
}

func double(value: Int) throws -> Int {
    guard value <= Int.max / 2 else {
        throw SomeError.overInt
    }
    
    return value * 2
}


let value1 = try! double(value: 100)
print(value1)
let value2 = try! double(value: Int.max)   // 実行時エラー
print(value2)

実行結果
200
Fatal error: 'try!' expression unexpectedly raised an error: __lldb_expr_297.SomeError.overInt: file __lldb_expr_297/MyPlayground.playground, line 17
```

try!キーワードは、Swift自慢の安全性を損ねるのでは？と思いましが、よく考えると、
**いかなる場合も全てのエラーケースに対処するのはあまりにも面倒だなとも思います。**
それに現実的ではありませんし可読性も悪くなります。

かといって暗黙的にエラーが無視できると考えると、
**どこでエラーが無視されたのかわからず、
結果として信頼性の低いプログラムにもなります。**

ですが、「！」がついている箇所が暗黙的にエラーを無視する箇所とわかるので、
**結果としてtry!キーワードは実用性と安全性をバランスよく保っています。**

###try?キーワード

throwsキーワードが指定された処理であっても、
**利用時にはエラーの詳細が不要な場合もあります。**
try?キーワードはそのような時に使用します。

**try!キーワードは、エラーは絶対に発生しない時に使い。
try?キーワードは、エラーは発生するが詳細は不要な時に使うイメージです。**

**try?キーワードをつけて処理を呼び出すとdo-catch文を省略することができます。**
しかし、その代わりに関数の戻り値が**Optional\<Wrapped>型**になります。

つまり、Optional\<Wrapped>型によるエラー処理と同様になります。

tryキーワードとの相違点としては、
do-catch文のdo節やthrowsキーワードが指定された内部でなくても使用できるところです。

次のサンプルコードでは、
引数の値を２倍にして返す関数を定義しています。

②の`if let value = try? double(value: Int.max){}`は、
guard文で引っ掛かり`throw SomeError.overInt`が実行されますが、
実際にvalueに入る値はnilになります。


```swift

enum SomeError: Error {
    case overInt
}

func double(value: Int) throws -> Int {
    guard value <= Int.max / 2 else {
        throw SomeError.overInt
    }
    
    return value * 2
}

// ①
if let value = try? double(value: 2) {
    print(value)
} else {
    print("error")
}

// ②
if let value = try? double(value: Int.max) {
    print(value)
} else {
    print("error")
}

実行結果
4
error
```

また、try?キーワードはイニシャライザに対しても利用できるので、
失敗可能イニシャライザのように扱うこともできます。


```swift

enum SampleEnum: Error {
    case outOfRange
}

struct SampleStruct {
    var value: Int
    
    init(value: Int) throws {
        guard case 10...19 = value else {
            throw SampleEnum.outOfRange
        }
        self.value = value
    }
}

if let value = try? SampleStruct(value: 13) {
    print(value)
} else {
    print("error")
}

if let value = try? SampleStruct(value: 20) {
    print(value)
} else {
    print("error")
}

実行結果
SampleStruct(value: 13)
error
```

###defer文によるエラーの有無に関わらない処理

defer文を使用すれば、エラーが発生しようと発生しなかろうと処理を行うことができます。
記述方法はとても簡単で実行したい箇所に追加するだけです。

まず、defer文を使わない場合の処理がこちらになります。

`try sampleFunc()`で必ずエラーが発生するので、
`hello()`関数は絶対に実行されません。

```swift

enum SampleError: Error {
    case someError
}

func sampleFunc() throws {
    print("sampleFunc()実行")
    throw SampleError.someError
}

func hello() {
    print("こんにちは")
}

do {
    try sampleFunc()
    hello()
} catch {
    print("Error: \(error)")
}

実行結果
sampleFunc()実行
Error: someError
```

次がdefer文を使用した時の処理です。

```swift

enum SampleError: Error {
    case someError
}

func sampleFunc() throws {
    print("sampleFunc()実行")
    throw SampleError.someError
}

func hello() {
    print("こんにちは")
}

do {
    defer {
        hello()
    }
    try sampleFunc()
} catch {
    print("Error: \(error)")
}

実行結果
sampleFunc()実行
こんにちは
Error: someError
```

defer文の使えるところは、
do節が終わった後に実行されるというところです。

最後に行ってほしい処理があった際には便利そうですね！

以上でtryキーワード関連の説明は終了したいと思います。
do-catchは難しいですがぜひ使えるようになりたいです・・・。

続きは**tryキーワードの利用タイミング**について説明していますのでぜひご覧ください。
-> [【Swift】do-catch文でエラー処理を行う（その３）](https://qiita.com/onishiApp/items/8ef921ab8b9ca82bf394)

また、他のエラー処理についても記事にしているのでぜひご覧ください。
・[【Swift】Optional\<Wrapped>型でエラー処理を行う](https://qiita.com/onishiApp/items/110cc714139cd3dc7507)
・[【Swift】Result\<Success, Failure>型でエラー処理を行う](https://qiita.com/onishiApp/items/76e66d1504fcf42a9136)
・[【Swift】fatalError関数によるプログラムの終了](https://qiita.com/onishiApp/items/e6f8cd28fe2ea38ebb7a)
・[【Swift】アサーションによるプログラムの終了](https://qiita.com/onishiApp/items/66d65f7b56ae178b52b8)

最後までご覧いただきありがとうございました。