It might be helpful to think about how a good solution would look like to achieve the semantics of rethrows
.
Let's consider the strictest version (the one @xwu talks about).
func f(g: () throws -> Void) rethrows
f
throws the exactly same error what g
throws. Not the other type, not the same type, but the same exact value.
It means the signature of f
declares "Data Flow". It describes how values will be passed. It's much more detailed function declaration, than Swift is designed for. (IMO it's too much detailed).
How may more generalized "Data Flow" declarations look like in signatures of functions?
One bad thing about rethrows
is that it doesn't explicitly specify the "source" of the error value.
Let's fix this:
func f<E: Error>(g: () throws(a: E) -> Void) throws(a)
So, a
is an arbitrary name for a value of error, that will be thrown by g
.
throws(a: E)
means that the type of error will be E
, but also defines a
as a named source.
throws(a)
means it will throw only errors sourced from whatever the source a
.
It's a stricter declaration than throws(E)
.
Under such limitation the following code would be invalid:
var cache: Any?
func f<E: Error>(g: () throws(a: E) -> Void) throws(a) {
if let e = cache as? E {
throw e // error: operand is expected to be sourced by `a`
}
do {
try g()
} catch {
cache = error
throw error
}
}
And we can generalize this notion to return values as well.
So we explicitly specify relation between the inner function and the outer function in terms of values.