Compiler cannot infer rethrow error type when calling rethrows function inside throws(e)
The rethrows telling us “Error type thrown is the same passed closure throws”.
But in this simplified example in some reason it cannot infer that body error type is exact, not any Error.
What I’m missing?
Are there suggestions on using rethrows functions (like DispatchQueue.sync) inside typed-throw ones.
If E is inferred to be Never, you don't need try at call site, in effect making both of them non-throwing, when passing a non-throwing argument. Is this not working for you?
I believe this is because rethrows doesn’t technically require that the error thrown is the same error the closure threw, just that the error is not thrown if the closure doesn’t throw. In other words, this is legal:
It's like that, but more specifically, that the only two types that can be thrown from rethrows are Never and any Error.
I.e. the normal solution with typed errors cannot save you:
func typedThrowFn<E>(_ body: () throws(E) -> Void) throws(E) {
try rethrowFn { () throws(E) in // Thrown expression type 'any Error' cannot be converted to error type 'E'
try body()
}
}
Normally, this means that rethrows is not helpful anymore, because typed throws can handle both of those, as a subset. But it still has a place. There is no way to represent the following with typed throws. rethrows can do it, though:
Throw a type-erased version of one of multiple error types, or throw Never, and therefore don't require try.
Swift doesn't have official code generation tools, so I wouldn't consider them the same from a maintenance point of view.
You've added the overload where the signature preserves a typed error thrown by both closures. But this does not handle these useful overloads where one closure throws Never, which should allow error type preservation as well. Swift has no mechanism for this aside from explicit overloads.* (This is why I haven't bothered ever incorporating matching error types, rather than use rethrows.)
struct E1: Error { }
struct E2: Error { }
let specializedF1 = f as (
() throws(E1) -> _,
() -> _
) throws -> _
try specializedF1(E1().throw) { } // Compiles.
try (f as (_, _) throws -> _)(E1().throw) { } // Compiles.
try f(E1().throw) { } // Type of expression is ambiguous without a type annotation
let specializedF2 = f as (
() throws(E1) -> _,
() throws(E2) -> _
) throws -> _
try specializedF2(E1().throw, E2().throw) // Compiles.
try (f as (_, _) throws -> _)(E1().throw, E2().throw) // Compiles.
try f(E1().throw, E2().throw) // Type of expression is ambiguous without a type annotation
* The specialized / cast functions have type-safe parameters, but the E becomes any Error, as you might expect, when multiple non-Never error types are present. And E is the non-Never error type, in the other case. It would be nice if this could become a "real" feature. But then again, I guess that goes for typed throws in general.