Sure, though this is used when the original error isn't an appropriate description of the problem at the current API level (e.g. FileManager
will wrap a POSIXError
with a CocoaError
because the fact that Cocoa's file management routines are implemented by POSIX APIs is an implementation detail and not something that FileManager
's clients should have to deal with). When my API involves decoding JSON into a model object, and and I get a decoding error, that decoding error is the appropriate thing to pass to my clients already, and wrapping it serves no purpose.
No I'm concerned that the actual error will be ignored. The error with semantic meaning got wrapped by one without, so unless you go digging into the underlying error, you actually lose meaning with this wrapping. I don't want to design an API that forces all of my users to dig into the underlying error in order to actually get the information they need, and I don't want the "obvious action" (taking the error and logging it to analytics) to produce a bad result.
Logging the original JSON in the same string as the actual error text is very explicitly a bad thing. That makes it really hard to coalesce error logs together or extract any meaningful information in aggregate. I want to log the actual error, which can then be handled appropriately in aggregate, and I want to attach the source text as secondary information that I can use if I dig into the individual logged errors as part of diagnosing the issue.
More generally, literally the only reason to have the Failure
type conform to Error
is because the Swift core team doesn't want to encourage people to use String
as their error type. That's it. There's no technical benefit, as we can trivially make get()
into a conditional extension. And I would argue that forcing the Failure
type to conform to Error
actually serves no purpose and is a holdover from thinking of this as a replacement for throws
.
throws
is specifically an error mechanism, and everything it throws should be an error. We already have a separate mechanism for returning one of two different value types (namely, a 2-variant enum with associated values), and the purpose of throws
is very explicitly to implement an error strategy, including propagating errors up the stack, along with explicit catch
blocks to catch them, etc.
Result
is not specifically an error mechansim. It's the representation of the outcome of a failable computation. The failure state for a computation may not necessarily be an error. It will probably include an error, though even that isn't a requirement. More generally, Result
does not directly tie into the rest of the error-handling mechanism. There is no automatic propagation of Result
s up the stack, no do
/catch
block construct that deals with it, and no automatic coalescing of multiple error types into a single existential (e.g. there's no built-in way to combine a Result<Int,FooError>
with a Result<Int,BarError>
). The fact that the Failure
type is constrained on Error
has no benefit and only makes Result
more awkward to use outside of the limited scenario of "I'm taking a throws
API and representing it with Result
".
Also, there's precedent in Cocoa for wanting to have Failure
not specifically conform to Error
. What I'm thinking about is URLSession.dataTask(with:completion:)
. This API cannot be converted to use Result
without retaining the ugly optional URLResponse?
type in the handler, even though we know for a fact that the success case includes a URLResponse
and we'd really like to make it non-optional. The ideal completion handler type here is (Result<(URLResponse, Data), (URLResponse?, Error)>) -> Void
, but this is not a type that can be written as long as Failure
is constrained to Error
.