I'm sure you heard this request like a billion times already, but I'd like to be official and make a proper proposal for that. So having said that, I hereby propose adding support for typed `throws` annotations.
If a function can throw, it is often known what type of error it may throw. Consider this piece of code:
enum NetworkError: ErrorType {
case RequestTimeout
case UnreachableHost
}enum SearchError: ErrorType {
case InvalidQuery
}func search(query: String) throws {}
func m() {
do {
try search("foo")
} catch SearchError.InvalidQuery {
print("your query is invalid")
} catch is NetworkError {
print("please check your internet connection")
} catch {
print("an unknown error occurred") // ???
}
}
In order for `do-catch` to be exhaustive, the vague `catch` block is required by the compiler, and inside of it, one has literally no idea what kind of error they deal with. This is useless or even dangerous for error handling, because the last `catch` often looks like this:
catch {
// don't know what that is, let's ignore that
}
The situation above can be resolved by introducing typed error handling, so that the `search` function may be refactored to:
func search(query: String) throws SearchError, NetworkError {}
Then, the requirement for last, opaque `catch` block can be completely removed as the compiler will make sure that only errors of the allowed types be thrown from `search`.
I will be happy to hear your comments on this.
–––––
As a bonus, I prepared a short FAQ for my proposal:
Q: What if my function throws multiple error types?
Use comma-separated list of error types.
func f() throws SomeError, OtherError {}
Q: What if I `try` a function which throws something completely different?
Then you have three possibilities:
1. You resolve the error in scope of the function, inside a `catch` block.
2. You convert it into a compatible type and `throw` it.
3. You annotate your `func` to be throwing that particular error as well.
Q: What about `rethrows`?
`rethrows` should become generic and throw anything that the closure arguments can throw.
func f(g: () throws SomeError -> Void, h: () throws OtherError -> Void) rethrows {}
// behaves like
func f(g: () throws SomeError -> Void, h: () throws OtherError -> Void) throws SomeError, OtherError {}
Q: What if I want to use the old behavior?
Just annotate as `throws ErrorType` and you can throw anything from there.
func f() throws ErrorType {}
Q: Is the failure path really as important as the success path?
Yes, it is.
Pozdrawiam – Regards,
Adrian Kashivskyy