rethrows
とは何か
rethrows
は関数の修飾子の一つです(throws
と同様です)。
rethrows
が指定された関数(ここでは便宜上、親関数と呼ぶことにします)は
引数として関数(同じく、子関数とします)を取ります。
子関数として渡された関数に throws
が指定されていた場合、
子関数がエラーをスローすると親関数はそのエラーをそのままスローします。
当然、親関数の呼び出し元には try
が必要です。
しかし子関数として渡された関数に throws
が指定されていなかった場合、
親関数がエラーをスローすることはありません。
この場合、親関数の呼び出し元には try
は不要です。
親関数の呼び出し元で子関数として渡した関数に throws
が指定されていたかどうかによって、
親関数の呼び出し元に try
が必要かどうかが変わるのです。
これが rethows
と throws
の違いです。
// 親関数の例
func parent(_ child: () throws -> Void) rethrows {
try child()
}
// throws が指定された子関数の例として使う関数
func childThrows() throws { }
// throws が指定されていない子関数の例として使う関数
func childDoesNotThrow() { }
try parent(childThrows)
// ^ 子関数に throws が指定されているため、親関数の呼び出しに try が必要。
parent(childDoesNotThrow)
// ^ 子関数に throws が指定されていないため、親関数の呼び出しに try が不要。
子関数がクロージャで、throws
の有無が明示されていない場合は、
暗黙的に throws
が指定されていると見なされるかどうかで決まります。
// 親関数の例
func parent(_ child: () throws -> Void) rethrows {
try child()
}
try parent { throw NSError() }
// ^ 子関数に throws が指定されていると見なされるため、親関数の呼び出しに try が必要。
parent { }
// ^ 子関数に throws が指定されていないと見なされるため、親関数の呼び出しに try が不要。
どのような時に rethrows
を指定するべきか
親関数に rethrows
を指定するべきなのは、次の全てを満たす場合です。
- (1) 関数を引数に取る関数である。
- (2) 親関数がスローするエラーは、子関数がスローしたエラーだけである。
- (3) 子関数が、
throws
が指定されている関数の場合もあれば、
throws
が指定されていない関数の場合もある。
(1)と(2)は、親関数に rethrows
を指定するための必要条件です。
(3)は必要条件ではありませんが、
子関数が常に throws
が指定された関数なのであれば rethrows
でなく throws
でよいですし、
子関数が常に throws
が指定されていない関数なのであれば rethrows
も throws
も不要です。
(1)(2)(3)を全て満たす場合に、rethrows
ではなく throws
を指定することもできますが、
それだと子関数がthrows
が指定されていない関数だった場合に、親関数がエラーをスローすることがなくなるにも関わらず、
親関数の呼び出し元に try
が必要となってしまいます。
rethrows
を指定した関数を定義する
親関数は次のように定義します。
-
throws
指定子を書くのと同じ場所にrethrows
指定子を書く。
func myFunc(arg: () throws -> String) rethrows -> String {
// ^^^^^^^^ throws を書くのと同じ場所に書く。
try arg()
}
- 子関数には(実際にはエラーをスローしない関数が渡されるかもしれないが)
throws
指定子を付ける。
func myFunc(arg: () throws -> Void) rethrows {
// ^^^^^^ throws を指定する。
try arg()
}
あとがき
rethrows
については、Swift 公式の LANGUAGE GUIDE にも書かれていないし、日本語のわかりやすい記事も見当たらなかったため、この記事を書きました。
誤りなどがございましたらご指摘いただけると幸いです。
/以上