SwiftのthrowsとtryをC++例外と比べて整理する

Swiftにもエラー処理があります。

見た目はC++例外に少し似ていますが、呼び出し側に try が出るので、失敗する処理が読みやすいです。

公式では、関数がエラーを投げる可能性を示すために throws を書き、呼び出し側では try を使うと説明されています。

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/

throwsを書く

enum LoadError: Error {
    case notFound
}

func loadName(id: String) throws -> String {
    if id.isEmpty {
        throw LoadError.notFound
    }
    return "Makonote"
}

throws が付いた関数は、呼び出し側で try が必要です。

let name = try loadName(id: "1")

do-catchで受ける

do {
    let name = try loadName(id: "1")
    print(name)
} catch {
    print(error)
}

C++なら try/catch に近いです。

try {
    auto name = loadName("1");
} catch (const std::exception& error) {
    ...
}

ただ、Swiftでは失敗する呼び出しに try が付くので、どこでエラーが出るか見えやすいです。

try?でOptionalにする

エラー内容までは使わず、成功した値だけ欲しい場合は try? を使えます。

let name = try? loadName(id: "")

この場合、nameString? です。

失敗したら nil になります。

原因を捨てるので、ログやユーザー表示が必要な処理では雑に使わない方がいいです。

Resultとの使い分け

Swiftには Result<Success, Failure> もあります。

let result: Result<String, Error> = .success("Makonote")

単純に呼び出して失敗を処理するなら throws が読みやすいです。

一方、成功/失敗を値として保持したい場合は Result が合います。

このあたりは SwiftでResultとthrowsを使い分ける目安 に分けています。

C++例外との感覚の違い

C++では、関数シグネチャだけを見ても例外を投げるか分かりにくいことがあります。

Swiftでは throws が型の一部として見えます。

func load() throws -> Data

呼び出し側にも try が出ます。

let data = try load()

この明示性がSwiftのエラー処理の読みやすさです。

まとめ

Swiftのエラー処理は、C++例外に近い構文を持ちつつ、失敗可能性が見えやすいです。

  • エラーを投げる関数には throws
  • 呼び出し側には try
  • 受けるなら do-catch
  • エラーをOptionalに潰すなら try?
  • 値として保持するなら Result

失敗理由を扱いたい処理では、try? でnilに潰すより、まず do-catch で受ける方が後から困りにくいです。

関連記事

参考