Skip to content(if available)orjump to list(if available)

Type-safe and user-friendly error handling in Swift 6

dcrazy

Some people might be surprised that statically typed errors have arrived so late in Swift’s life. This is because typed errors are a bad default. Without deep consideration, it is easy to design an error type that encodes too much implementation detail into the type system. Then if your implementation grows new failure modes, you either have to break your ABI (e.g. by adding a new case to your error enum) or lie about the nature of the error, calling into question the value of encoding errors in the type signature at all. And at a certain level of abstraction, it’s just impossible to predict all the failures that can occur. We learned these lessons from Java’s checked exceptions experiment.

ChrisMarshallNY

I’ve learned to keep errors simple. I have found that the most important thing is to anticipate and trap the error. Reporting it is actually the simplest part of the thing.

The kind of structure mentioned in the article, with things like contexts, and calling chains, is stuff that I did for years.

In my experience, it had a glass jaw. It broke easily. The more complicated something is, the more places it has to break.

Also, I found that I never, ever used it. The most introspection I ever did, was look at an error code. Where the error happened, was the important thing, and a symbolic debugger gave me that.

These days, I just declare an Error-conformant enum, sometimes with associated data, and add a few computed properties or functions.

It’s most important that I write my code to anticipate and catch errors. The report is always refined to be as simple and gentle to the user, as possible. The technical stuff, I have found, is best handled with a symbolic debugger.

But that’s just me, and I know that the type of software that I write, is best served with that model. YMMV.

rTX5CMRXIfFG

Agree with the point on statically typed errors. However, hasn’t it been possible to define a custom Error type since Swift 1? Or did you mean the typed throws?

Have also been picking up Java and the thing that comes to my mind is sealed classes, and whether they are similarly bad design.

MBCook

It’s typed throws specifically.

Before Swift 6 a function either could throw nothing or could throw anything, like Java’s “throws Exception”.

Now you can mark that a function can only throw a specific error type but nothing else, like Java’s “throws MyCustomException”.

Seems you can’t list multiple types. You’d have to have them in a class hierarchy and mark the base type.

What I like about this idea: if a function is marked like this, Swift can tell that a catch block is exhaustive without needing a “catch all” error case. And if the function starts throwing more possibilities than you coded for, e.g. new case added to an enum, you get a compiler error! Not a submarine bug.

dcrazy

That last bit is great for application authors but awful for API designers. :)

jayd16

Why do you need to restrict to a single base type to exhaustively catch all listed?

dcrazy

The new feature here is typed throws, which I referred to as statically typed errors. You’ve always been able to throw a subtype of Error and then dynamically downcast it at runtime, but function signatures were limited to “throws any subtype of Error” or “doesn’t throw at all.”

Sealed classes fall into the realm of things that the Swift language designers would leave up to convention. For example, using underscored public methods for internal methods that need to have external linkage, rather than inventing a new keyword for this purpose.

brundolf

I like Rust's approach, where common "errors" (eg unexpected responses from other systems) have to be handled, while true unrecoverable errors don't affect the signature