1. ホーム
  2. swift

[解決済み] Swiftのthrowとrethrowsの違いは何ですか?

2023-04-25 18:50:23

質問

いくつかの文献を検索してみたのですが、残念ながら throwsrethrows . これらの使い分けを理解しようとすると、ちょっと混乱します。

私は -default- の使い方に慣れていることを述べておきます。 throws を、エラーを伝播させるための最も単純な形として、以下のように記述します。

enum CustomError: Error {
    case potato
    case tomato
}

func throwCustomError(_ string: String) throws {
    if string.lowercased().trimmingCharacters(in: .whitespaces) == "potato" {
        throw CustomError.potato
    }

    if string.lowercased().trimmingCharacters(in: .whitespaces) == "tomato" {
        throw CustomError.tomato
    }
}

do {
    try throwCustomError("potato")
} catch let error as CustomError {
    switch error {
    case .potato:
        print("potatos catched") // potatos catched
    case .tomato:
        print("tomato catched")
    }
}

ここまではいいのですが、問題は次のときです。

func throwCustomError(function:(String) throws -> ()) throws {
    try function("throws string")
}

func rethrowCustomError(function:(String) throws -> ()) rethrows {
    try function("rethrows string")
}

rethrowCustomError { string in
    print(string) // rethrows string
}

try throwCustomError { string in
    print(string) // throws string
}

という関数を呼び出すと、その関数の中で throws で処理されなければならないことです。 try とは異なり rethrows . だから何なんだ!?を使うかどうかを決めるときに従うべき論理は何でしょうか? throws あるいは rethrows ?

どのように解決するのですか?

から "宣言" をSwiftの本で見てみましょう。

関数とメソッドの再投入

関数やメソッドは rethrows キーワードで宣言することができます。 関数のパラメータがエラーを投げるときだけエラーを投げることを示すために パラメータがエラーを投げる場合のみエラーを投げることを示します。これらの関数やメソッドは 再投げ関数 と呼ばれ メソッドの再投入 . 再投げ関数と メソッドは少なくとも一つのスローイングファンクションパラメータを持っていなければなりません。

典型的な例としては map メソッドです。

public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

もし map がスローしないトランスフォームで呼ばれた場合、それ自身はエラーをスローしません。 を投げないので try :

// Example 1:

let a = [1, 2, 3]

func f1(n: Int) -> Int {
    return n * n
}

let a1 = a.map(f1)

しかし、もし map がスローするクロージャで呼ばれた場合は、それ自体がスローする可能性があります。 で呼び出されなければなりません。 try :

// Example 2:

let a = [1, 2, 3]
enum CustomError: Error {
    case illegalArgument
}

func f2(n: Int) throws -> Int {
    guard n >= 0 else {
        throw CustomError.illegalArgument
    }
    return n*n
}


do {
    let a2 = try a.map(f2)
} catch {
    // ...
}

  • もし map と宣言された場合 throws ではなく rethrows というように で呼び出さなければなりません。 try で呼び出さなければなりません。 これは不便であり、コードを不必要に肥大化させます。
  • もし map がない状態で宣言された場合 throws/rethrows がない場合は、例2のように で宣言されている場合は、例2のようにスローイングクロージャで呼び出すことはできません。

同じことが、Swift 標準ライブラリの他のメソッドにも当てはまります。 の他のメソッドも同様です。 filter() , index(where:) , forEach() などなど。

あなたの場合

func throwCustomError(function:(String) throws -> ()) throws

は、エラーを投げない引数で呼ばれてもエラーを投げることができる関数を表します。 投げない引数で呼ばれてもエラーを投げる可能性がある関数を表します。

func rethrowCustomError(function:(String) throws -> ()) rethrows

を付けて呼ばれた場合のみエラーを投げる関数を表します。 を投げる引数で呼ばれた場合のみエラーを投げる関数を表します。

大雑把に言うと rethrows は、エラーを投げない関数のためのものです。 エラーを投げるのではなく、関数のパラメータからエラーを転送するだけです。 パラメータからエラーを転送するだけです。