Menu
Coddy logo textTech

エラーの伝播

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")
}

上から下へ読みます:logintry を使用して loadUser を呼び出します。もし loadUser が例外をスローした場合、エラーは login の本体をスキップして外側の catch に到達します。各ステップをフェイルファスト(早期失敗)するように記述し、呼び出し側にどう処理するかを委ねます。

challenge icon

チャレンジ

中級

2つのスロー(throwing)ステップを持つ小さなパイプラインを構築してください。

2つのケース negativetooBig を持つ 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 がスローした場合は negative
  • capStrict がスローした場合は 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して、表示する
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

ロジックとフローのすべてのレッスン