#do-catch文
do-catch文を使ったエラー処理では、
エラーが発生する可能性がある処理をdo節内に記述し、
エラーが発生するとcatch節に移動します。
なお、catch節内ではエラーの詳細情報にアクセスできるので、
Result<Success, Failure>型と同様にエラー詳細を用いた処理を行うことができます。
do-catch、優秀ですね!
##実装方法
do-catch文では、throw文によるエラーが発生し得る処理をdo節内に記述し、
catch節にエラー処理を記述します。
throw文はエラーを発生させる文で、
Errorプロトコルに準拠した値を使います。
また、catch節では暗黙的に宣言された定数errorを使うことができます。
do {
throw文によるエラーが発生する可能性のある処理
} catch {
エラー処理
定数errorを通じてエラー値にアクセス可能
}
次のサンプルコードでは、
Errorプロトコルに準拠したSomeError型を定義し、エラーを発生させています。
print( )が実行される前にcatch節へ移行するため、
do節のprint( )でなく、catch節のprint( )が実行されます。
struct SomeError: Error {}
do {
throw SomeError()
print("Success")
} catch {
print("Failure: \(SomeError())")
}
実行結果
Failure: SomeError()
catch節ではエラーを分けることもできます。
分ける方法としては、パターンマッチを使います。
パターンマッチの文法はSwitch文と同様で、
全てのケースを網羅しなければならなかったり、
defaultキーワードの役割のcatch節も存在します。
enum SomeError: Error {
case error
case warning(String)
}
do {
throw SomeError.warning("なんか変だよ?")
} catch SomeError.error {
print("Error")
} catch SomeError.warning(let reason) {
print("Warning: \(reason)")
} catch {
print(error)
}
実行結果
Warning: なんか変だよ?
###Errorプロトコル
throw文のエラーを表現する型は、
Errorプロトコルに準拠している型だということは冒頭で説明しました。
型は基本的に列挙型で定義します。
それにも理由があり、発生するエラーを網羅的に記述できるからです。
また、プログラム全体で起こり得るあらゆるエラーを1つの型に記述するのではなく、
エラーの種類ごとに型を定義することが一般的らしいです。
この理由としては、パターンマッチを記述する時に、
全てのケースを記述する必要があるからだと勝手に思っています。
###throwsキーワード
throwsキーワードは関数やイニシャライザ、クロージャの定義に追加します。
そうすることにより、
do-catch文を用いずにthrow文によるエラー処理を発生させることができます。
func 関数名(引数) throws -> 戻り値の型 {
throw文によるエラーが発生する可能性のある処理
}
次のサンプルコードでは、
引数に貰った値を2倍にして返す関数を定義しています。
2倍した際にInt型の許容範囲を超えてしまう可能性があるので、
guard文でint <= Int.max / 2
のように確認しています。
Int.max / 2の値よりも引数の値が大きかった場合は
エラー処理を行う流れになっております。
enum OperationError: Error {
case overCapacity
}
func double(value int: Int) throws -> Int {
guard int <= Int.max / 2 else {
throw OperationError.overCapacity
}
return int * 2
}
throwsキーワードはイニシャライザにも使用できます。
イニシャライザで使う場合は、
インスタンス化の途中で発生したエラーを呼び出し元に伝えることができます。
次のサンプルコードでは、
引数で貰った値が奇数だった場合はエラーを起こすようにしています。
enum CheckValue: Error {
case valueIsOdd
}
struct Sample {
let value: Int
init(value: Int) throws {
guard value % 2 == 0 else {
throw CheckValue.valueIsOdd
}
self.value = value
}
}
###rethrowsキーワード
この記事を書くにあたり初めて聞いたキーワードなのですが、
rethrowsキーワードは、関数やメソッドをrethrowsキーワードを指定して定義することで、
引数のクロージャが発生させるエラーを関数の呼び出し元に伝播させる事ができるらしいです。
つまり、rethrowsキーワードを指定する場合は、
最低でも1つのエラーを発生させるクロージャを引数にとる必要があります。
つまり、下記のサンプルコードの状態です。
実際に書いてみたけど難しい!!(笑)
関数のdivision( )は第一引数にInt型の値を、第二引数にクロージャをもらいます。
そして偶数だった場合は2で割った値を戻り値に、奇数だった場合はエラー処理を行います。
(tryについてはこの記事の続きで説明します。)
割り切れる値だった場合は、
throw SomeError.valueIsOdd
が実行されずにvalueの値が2で破られた値になります。
enum SomeError: Error {
case valueIsOdd
}
func division(value: Int, closure: (_ value: Int) throws -> Void) rethrows -> Int {
// 引数で貰ったvalueを2で割って余りが0ではなかったらguard内を実行
guard value % 2 == 0 else {
// クロージャを実行
try closure(value)
return value
}
return value / 2
}
var value = 0
do {
// エラーを起こす可能性があるのでdo節内に記述する
try value = division(value: 11, closure: { (value) in
// 割り切れなかったらクロージャが実行される
throw SomeError.valueIsOdd
})
} catch {
print(error)
}
print(value)
実行結果
valueIsOdd
0
まだ実装方法の途中ですがキリがいいのでここまでにします。
続きはtryキーワードについて説明していますのでぜひご覧ください。
-> 【Swift】do-catch文でエラー処理を行う(その2)
また、他のエラー処理についても記事にしているのでぜひご覧ください。
・【Swift】Optional<Wrapped>型でエラー処理を行う
・【Swift】Result<Success, Failure>型でエラー処理を行う
・【Swift】fatalError関数によるプログラムの終了
・【Swift】アサーションによるプログラムの終了
最後までご覧いただきありがとうございました。