はじめに
Swift の rethrows
を使うと、クロージャが throws
を投げるかどうかに応じて関数の throws
の有無が決まる という、便利なエラーハンドリングができます。しかし、「throws
とは何が違うの?」「いつ rethrows
を使うべき?」と疑問に思う方も多いのではないでしょうか。
1. rethrows
とは?
rethrows
は 「関数本体はエラーを投げないが、渡されたクロージャが throws
の場合のみ throws
になる」 という意味を持ちます。
通常の throws
を使った関数と比べて、無駄な try
を省略できる のが特徴です。
rethrows
の例
func perform(operation: () throws -> Void) rethrows {
try operation()
}
この perform
関数は、渡される operation
が throws
なら perform
も throws
になり、そうでなければ throws
なしで呼び出せます。
使い方の違い
(1) throws
なしのクロージャ
perform {
print("エラーを投げない処理")
} // try 不要
(2) throws
ありのクロージャ
do {
try perform {
throw NSError(domain: "ExampleError", code: 1, userInfo: nil)
}
} catch {
print("エラー: \(error)")
}
渡すクロージャが throws
なので、try
が必要になります。
2. throws
との違い
通常の throws
を使う場合と比較すると、rethrows
のメリットがよく分かります。
(1) throws
を使う場合
func perform(operation: () throws -> Void) throws {
do {
try operation()
} catch {
throw NSError(domain: "ExampleError", code: 1, userInfo: nil)
}
}
この場合、operation
が throws
でなくても、perform
は常に throws
になるので、呼び出し時に必ず try
をつける必要があります。
do {
try perform {
print("エラーを投げない処理")
} // try 必須!
} catch {
print("エラー: \(error)")
}
🔻 無駄な try
が発生してしまう!
(2) rethrows
を使う場合
func perform(operation: () throws -> Void) rethrows {
try operation()
}
この場合、
-
operation
がthrows
ならtry
が必要。 -
operation
がthrows
でなければtry
なしで呼べる。
perform {
print("エラーを投げない処理")
} // try 不要
✔ エラーを投げない場合に try
を省略できるので、コードがスッキリ!
3. rethrows
を活用するシチュエーション
Swift の標準ライブラリでも、rethrows
はさまざまな場面で活用されています。
(1) compactMap(_:)
let numbers = ["1", "2", "three", "4"]
let result = numbers.compactMap { str in Int(str) }
print(result) // [1, 2, 4]
compactMap
は rethrows
を使っているため、クロージャが throws
なら try
が必要ですが、そうでなければ try
なしでOK。
let result = try numbers.compactMap { str -> Int? in
if let num = Int(str) {
return num
} else {
throw NSError(domain: "InvalidNumber", code: 0, userInfo: nil)
}
}
(2) カスタム関数で rethrows
を活用
func execute(_ task: () throws -> Void) rethrows {
try task()
}
これを使うと、
execute {
print("エラーなしの処理")
} // try 不要
do {
try execute {
throw NSError(domain: "ExampleError", code: 1, userInfo: nil)
}
} catch {
print("エラー: \(error)")
}
✔ 不要な try
を減らしつつ、エラーを投げる場面だけ try
を強制できる!
4. まとめ
特性 | rethrows |
throws |
---|---|---|
クロージャが throws の場合 |
throws になる |
throws になる |
クロージャが throws でない場合 |
throws にならない(try 不要) |
throws のため try 必須 |
例外の変換 | そのままスロー |
catch で処理可能 |
rethrows
を使うメリット
✅ クロージャが throws
の場合だけ try
を要求
✅ エラーを投げない場合に try
を省略できるので、コードがスッキリ!
✅ Swift 標準ライブラリでも compactMap
などで活用
📝 補足メモ
-
rethrows
は関数の本体でthrow
することはできない -
rethrows
を使う場合、クロージャのthrows
をそのまま伝搬するだけ