エラーの伝播
CoddyのSwiftジャーニー「ロジックとフロー」セクションの一部 — レッスン 47/56。
エラーを投げる関数は、他のエラーを投げる関数を呼び出すことができます。エラーは自動的に最も近い do/catch まで伝播するため、手動で再スローする必要はありません。
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // エラーを転送する
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}上から下へ読みます:login は try を使用して loadUser を呼び出します。もし loadUser が例外をスローした場合、エラーは login の本体をスキップして外側の catch に到達します。各ステップをフェイルファスト(早期失敗)するように記述し、呼び出し側にどう処理するかを委ねます。
チャレンジ
中級2つのスロー(throwing)ステップを持つ小さなパイプラインを構築してください。
2つのケース negative、tooBig を持つ StepError: Error を定義します。
parseSafe(_ s: String) throws -> Int を定義します:パースされた整数が 0 未満の場合は .negative をスローし、それ以外の場合はその値を返します。(入力はパース可能であると想定してください。その処理は他で行われているものとします。)
capStrict(_ n: Int) throws -> Int を定義します:n > 100 の場合は .tooBig をスローし、それ以外の場合は n を返します。
parseSafe を呼び出した後に capStrict を呼び出し、エラーを伝播させる processStep(_ s: String) throws -> Int を定義します。
カンマ区切りの文字列リストである1行の入力を読み取ります。各文字列に対して、do/catch 内で processStep を呼び出し、1行ずつ出力してください:
- 成功時は
ok: <n> parseSafeがスローした場合はnegativecapStrictがスローした場合はtoo big
入力が 10,-1,200,42 の場合、出力は以下のようになります:
ok: 10
negative
too big
ok: 42チートシート
スロー関数は、try を使用して他のスロー関数を呼び出すことができます。エラーは、最も近い do/catch まで自動的に伝播します:
enum AuthError: Error { case badUser, badPass }
func loadUser(_ name: String) throws -> String {
if name.isEmpty { throw AuthError.badUser }
return name
}
func login(_ name: String, _ pass: String) throws -> String {
let u = try loadUser(name) // error propagates up if thrown
if pass.count < 4 { throw AuthError.badPass }
return "hello \(u)"
}
do {
print(try login("", "abcd"))
} catch AuthError.badUser {
print("missing user")
} catch AuthError.badPass {
print("weak password")
}loadUser がエラーを投げた場合、login の残りの実行はスキップされ、直接外側の catch に到達します。手動で再スローする必要はありません。
自分で試してみよう
let parts = readLine()!.components(separatedBy: ",")
// TODO: enum StepError; parseSafe; capStrict; processStep;
// 各パーツについて、processStepをdo/tryで実行し、各ブランチをcatchして、表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。