55

Many modern languages provide rich exception handling features, but Apple's Swift programming language does not provide an exception handling mechanism.

Steeped in exceptions as I am, I'm having trouble wrapping my mind around what this means. Swift has assertions, and of course return values; but I'm having trouble picturing how my exception-based way of thinking maps to a world without exceptions (and, for that matter, why such a world is desirable). Are there things I can't do in a language like Swift that I could do with exceptions? Do I gain something by losing exceptions?

How for example might I best express something like

try:
    operation_that_can_throw_ioerror()
except IOError:
    handle_the_exception_somehow()
else:
     # we don't want to catch the IOError if it's raised
    another_operation_that_can_throw_ioerror()
finally:
    something_we_always_need_to_do()

in a language (Swift, for example) that lacks exception handling?

orome
  • 703
  • 1
  • 5
  • 10
  • 14
    [You may want to add Go to the list](http://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right), if we just ignore `panic` which is not quite the same. In addition what is said there, an exception is not much more than a sophisticated (but comfortable) way to perform a `GOTO`, although nobody calls it that way, for obvious reasons. – JensG Oct 03 '14 at 18:51
  • Swift doesn't have garbage collection; I would imagine that has something to do with it. – Robert Harvey Oct 03 '14 at 18:51
  • 3
    The sort answer to your question is that you need language support for exceptions in order to write them. Language support generally includes memory management; since an exception can be thrown anywhere and caught anywhere, there needs to be a way to dispose of objects that doesn't rely on the control flow. – Robert Harvey Oct 03 '14 at 19:07
  • 1
    I'm not quite following you, @Robert. C++ manages to support exceptions without garbage collection. – Karl Bielefeldt Oct 03 '14 at 19:14
  • 3
    @KarlBielefeldt: At great expense, from what I understand. Then again, is there *anything* that's undertaken in C++ without great expense, at least in effort and required domain knowledge? – Robert Harvey Oct 03 '14 at 19:15
  • 2
    @RobertHarvey: Good point. I'm one of those folks who doesn't think hard enough about these things. I've been lulled into thinking that ARC is GC, but, of course, it's not. So basically (if I'm grasping, roughly), exceptions would be a messy thing (C++ notwithstanding) in a language where disposing of objects relied on control flow? – orome Oct 03 '14 at 19:16
  • 1
    @raxacoricofallapatorius: Yes. Go eschewed exceptions because they felt that the complexity outweighed the benefit. Objective C had exceptions for awhile, but Apple deprecated them, partly because they didn't want to support them in iOS. See http://www.quora.com/Why-doesnt-Apple-Swift-adopt-the-memory-management-method-of-garbage-collection-like-in-Java – Robert Harvey Oct 03 '14 at 19:17
  • @RobertHarvey: Interesting. I didn't actually expect to understand the reasons, but I think I do. – orome Oct 03 '14 at 19:22
  • @RobertHarvey: And this means that Python (or any language with an exception handling mechanism) must (effectively) have GC? – orome Oct 03 '14 at 19:34
  • Or some elaborate mechanism that captures the relevant memory semantics and gracefully disposes the involved objects in the call stack. – Robert Harvey Oct 03 '14 at 19:36
  • @JensG: So Go and Swift follow the same strategy, essentially (with `assert` in Swift serving roughly for Go's `panic`)? – orome Oct 03 '14 at 19:43
  • 1
    @JensG actually Go have `panic`, `defer` and `recover` – Paweł Prażak Feb 24 '16 at 22:45
  • @PawelPrazak: I know. IMHO the Go error handling system started with good intentions but failed miserably: ["*error handling is important. The language's design and conventions encourage you to explicitly check for errors where they occur*"](http://blog.golang.org/error-handling-and-go) - well, that's an quite accurate description of what does **not** happen in real world go code. I can't count how many bugs I have seen related to Go error handling, the single most problem being ignoring errors by assigning to `_` - that's for the "*design/conventions encourage to explicitly check for errors*" – JensG Feb 25 '16 at 00:23
  • Aside from that, there are some things you cannot catch ... err, I mean of course recover from. So IMHO they should have simply sticked with what people are used to. The price of bloated source code paid for an error handling system w/o any significant improvements over exceptions is too high in my opinion. – JensG Feb 25 '16 at 00:23
  • There's zero connection between GC and exceptions. Python doesn't really have a GC either; it uses automatic reference counting. – Sebastian Redl May 31 '16 at 10:12
  • Having used Haskell for a bit now, I realize this is a backwards question (Julia notwithstanding): why design a modern language *with* exceptions? – orome Jul 12 '21 at 13:16

8 Answers8

38

In embedded programming, exceptions were traditionally not allowed, because the overhead of the stack unwinding you have to do was deemed an unacceptable variability when trying to maintain real-time performance. While smartphones could technically be considered real time platforms, they are powerful enough now where the old limitations of embedded systems don't really apply anymore. I just bring it up for the sake of thoroughness.

Exceptions are often supported in functional programming languages, but so rarely used that they may as well not be. One reason is lazy evaluation, which is done occasionally even in languages that are not lazy by default. Having a function that executes with a different stack than the place it was queued to execute makes it difficult to determine where to put your exception handler.

The other reason is first class functions allow for constructs like options and futures that give you the syntactic benefits of exceptions with more flexibility. In other words, the rest of the language is expressive enough that exceptions don't buy you anything.

I'm not familiar with Swift, but the little I've read about its error handling suggests they intended for error handling to follow more functional-style patterns. I've seen code examples with success and failure blocks that look very much like futures.

Here's an example using a Future from this Scala tutorial:

val f: Future[List[String]] = future {
  session.getRecentPosts
}
f onFailure {
  case t => println("An error has occured: " + t.getMessage)
}
f onSuccess {
  case posts => for (post <- posts) println(post)
}

You can see it has roughly the same structure as your example using exceptions. The future block is like a try. The onFailure block is like an exception handler. In Scala, as in most functional languages, Future is implemented completely using the language itself. It doesn't require any special syntax like exceptions do. That means you can define your own similar constructs. Maybe add a timeout block, for example, or logging functionality.

Additionally, you can pass the future around, return it from the function, store it in a data structure, or whatever. It's a first-class value. You're not limited like exceptions which must be propagated straight up the stack.

Options solve the error handling problem in a slightly different way, which works better for some use cases. You're not stuck with just the one method.

Those are the sorts of things you "gain by losing exceptions."

Karl Bielefeldt
  • 146,727
  • 38
  • 279
  • 479
  • 1
    So `Future` is essentially a way of examining the value returned from a function, without stopping to wait for it. Like Swift, it's return-value based, but unlike Swift, the response to the return value can occur at a later time (a bit like exceptions). Right? – orome Oct 04 '14 at 20:52
  • 1
    You understand a `Future` correctly, but I think you might be mischaracterizing Swift. See the first part of [this stackoverflow answer](http://stackoverflow.com/a/24030038/389146), for example. – Karl Bielefeldt Oct 04 '14 at 20:57
  • Hmm, I'm new to Swift, so that answer is a bit hard for me to parse. But if I'm not mistaken: that essentially passes a handler that *can* be invoked at a later time; right? – orome Oct 04 '14 at 21:09
  • Yes. You're basically creating a callback when an error occurs. – Karl Bielefeldt Oct 04 '14 at 21:14
  • `Either` would be a better example IMHO – Paweł Prażak Feb 25 '16 at 17:49
  • Would you be VERY offended if I ask you to support your statement that "In embedded programming, exceptions were traditionally not allowed, because the overhead of the stack unwinding you have to do was deemed an unacceptable variability when trying to maintain real-time performance."? Ada was specifically designed for embedded programming, and it specifically included exceptions. Ada also included a design goal: If you don't throw an exception, you shouldn't pay any exception penalty. (Yes, you have SOME penalty for unwinding the stack, but you're paying that anyway on CALL/RETURN.) – John R. Strohm Aug 28 '16 at 17:17
  • @JohnR.Strohm this is from over 10 years working on embedded projects in Ada and C++ in two separate companies in two separate industries, private and government contractor, where by policy we weren't allowed to use exceptions. A stack unwind from an exception is *not* identical to a return from a function call. It has the the potential to go several levels at once, it carries extra information such as a stack trace and error message that must be preserved, and they are frequently very difficult to trigger in order to validate you can handle the additional timing required on actual hardware. – Karl Bielefeldt Aug 28 '16 at 17:38
  • Even in functional languages exceptions make a lot of sense for dealing with truly unexpected errors in SOME way... it doesn't take the most comprehensibly understandable stack trace to make them useful for the most fundamental of problems your code might run into :) Especially when you're dealing with terrible things like the outside world with all the OS APIs and I/O and other terrible things that can go terribly wrong Falling back to error numbers is definitely not the answer, EVER. Except, as you said, in hard real-time systems, of course ^^ – yeoman Mar 29 '17 at 13:05
  • The lack of exceptions makes the languages less readable and maintainable, unfortunately. Problems described can be resolved. See Herb Sutter – JFFIGK Oct 17 '20 at 14:17
24

Exceptions can make code more difficult to reason. While they aren't quite as powerful as gotos, they can cause many of the same problems due to their non-local nature. For example, let's say you have a piece of imperative code like this:

cleanMug();
brewCoffee();
pourCoffee();
drinkCoffee();

You can't tell at a glance whether any of these procedures can throw an exception. You have to read the documentation of each of these procedures to figure that out. (Some languages make this slightly easier by augmenting the type signature with this information.) The above code will compile just fine regardless of whether any of the procedures throw, making it really easy to forget to handle an exception.

Additionally, even if the intent is to propagate the exception back to the caller, one often needs to add additional code to prevent things from being left in an inconsistent state (e.g. if your coffeemaker breaks, you still need to clean up the mess and return the mug!). Thus, in many cases code that uses exceptions would look just as complex as as code that didn't because of the extra cleanup required.

Exceptions can be emulated with a sufficiently powerful type system. Many of the languages that avoid exceptions use return values to get the same behavior. It's similar to how it's done in C, but modern type systems usually make it more elegant and also harder to forget to handle the error condition. They may also provide syntactic sugar to make things less cumbersome, sometimes almost as clean as it would be with exceptions.

In particular, by embedding error handling into the type system rather than implementing as a separate feature allows "exceptions" to be used for other things that aren't even related to errors. (It's well known that exception handling are actually a property of monads.)

Rufflewind
  • 2,217
  • 1
  • 15
  • 19
  • Is it correct that kind of type system that Swift has, including optionals, is the sort of "powerful type system" that achieves this? – orome Oct 04 '14 at 12:45
  • 2
    Yes, optionals and, more generally, sum types (referred to as "enum" in Swift/Rust) can achieve this. It takes some extra work to make them pleasant to use, however: in Swift, this is achieved with the optional chaining syntax; in Haskell, this is achieved with monadic do-notation. – Rufflewind Oct 04 '14 at 17:32
  • 3
    Can a "sufficiently powerful type system" give a stack trace, if not it's pretty useless – Paweł Prażak Feb 25 '16 at 17:41
  • 3
    +1 for pointing out that exceptions obscure the flow of control. Just as an auxilary note: It stands to reason whether exceptions are not actually more evil than `goto`: The `goto` is restricted to a single function, which is a pretty small range provided function really are small, the exception acts more like some cross of `goto` and `come from` (see https://en.wikipedia.org/wiki/INTERCAL ;-) ). It can pretty much connect any two pieces of code, possibly skipping over code some third functions. The only thing it can't do, which `goto` can, is going back. – cmaster - reinstate monica Aug 28 '16 at 15:10
  • 4
    @PawełPrażak When working with lots of higher-order functions, stack traces aren't nearly as valuable. Strong guarantees about inputs and outputs and avoidance of side effects are what prevent this indirection from causing confusing bugs. – Jack Aug 29 '16 at 18:11
  • "You can't tell at a glance whether any of these procedures can throw an exception." But in practice you assume that all of them can throw exception. If not today, then maybe next year when someone modifies the code, or maybe when one day the process runs out of free memory. If the language forces you to add `err != nil` or such for each call, you will just return the error to the caller, which is what exceptions basically do. If you have a step that needs cleanup, that's what try-with-resources in Java, or `with` in Python is for. Tools will even complain if you forget to use them. – ddekany Nov 02 '21 at 16:26
  • "But in practice you assume that all of them can throw exception." There are situations the calling code wants to exert more control over the throwability of a callee. In this context, errors do not include unhandleable failure modes like OOM or power outage. Languages that expose errors as first class values can still offer shortcuts when the default "escalate up the stack" strategy suffices, e.g. `do` notation in Haskell, `?` operators in C#/Rust/TypeScript. – Rufflewind Nov 04 '21 at 03:53
18

There are some great answers here, but I think one important reason has not been emphasized enough: When exceptions occur, objects can be left in invalid states. If you can "catch" an exception, then your exception handler code will be able to access and work with those invalid objects. That is going to go horribly wrongly unless the code for those objects was written perfectly, which is very, very difficult to do.

For example, imagine implementing Vector. If someone instantiates your Vector with a set of objects, but an exception occurs during the initialization (perhaps, say, while copying your objects into the newly allocated memory), it is very hard to correctly code the Vector implementation in such a way that no memory is leaked. This short paper by Stroustroup covers the Vector example.

And that is merely the tip of the iceberg. What if, for example, you had copied over some of the elements, but not all of the elements? To implement a container like Vector correctly, you almost have to make every action you take reversible, so the whole operation is atomic (like a database transaction). This is complicated, and most applications get it wrong. And even when it is done correctly, it greatly complicates the process of implementing the container.

So some modern languages have decided it is not worth it. Rust, for example, does have exceptions, but they cannot be "caught," so there is no way for code to interact with objects in an invalid state.

Charlie Flowers
  • 2,012
  • 2
  • 15
  • 11
  • 1
    the catch's purpose is to make an object consistent (or terminate something if it's not possible) after an error occurred. – JoulinRouge May 31 '16 at 08:13
  • 2
    @JoulinRouge I know. But some languages have decided not to give you that opportunity, but instead to crash the entire process. Those language designers know the kind of cleanup you'd like to do but have concluded it is too tricky to give you that, and the trade-offs involved in doing so would not be worth it. I realize you may not agree with their choice ... but it's valuable to know they made the choice consciously for these particular reasons. – Charlie Flowers Sep 23 '16 at 15:01
  • Taking the ability to catch away entirely makes swift and rust completely unsuitable for certain safety critical applications where it is preferable to report an error actively before orderly shutdown and restart of a process, as opposed to just indirectly reporting that the process has gone down via a keepalive timeout. A few seconds, or even just one tenth of a second, of avoidably delaying an error report can make a huge difference. – yeoman Jul 18 '21 at 10:07
  • It's such a common pattern to have one central catch statement outside the event loop sending out an alert before exiting that I'm stunned by this kind of stubbornness. What a dumb way to make the horrors of C and C++ reign supreme forever. – yeoman Jul 18 '21 at 10:09
9

In my opinion, exceptions are an essential tool for detecting code errors at run time. Both in tests and in production. Make their messages verbose enough so in combination with a stack trace you can figure out what happened from a log.

Exceptions are mostly a development tool and a way to get reasonable error reports from production in unexpected cases.

Apart from separation of concerns (happy path with only expected errors vs. falling through until reaching some generic handler for unexpected errors) being a good thing, making your code more readable and maintainable, it is in fact impossible to prepare your code for all possible unexpected cases, even by bloating it with error handling code to complete unreadability.

That's actually the meaning of "unexpected".

Btw., what is expected and what not is a decision that can only be made at the call site. That's why the checked exceptions in Java didn't work out - the decision is made at the time of developing an API, when it is not at all clear what is expected or unexpected.

Simple example: a hash map's API can have two methods:

Value get(Key)

and

Option<Value> getOption(key)

the first throwing an exception if not found, the latter giving you an optional value. In some cases, the latter makes more sense, but in others, your code sinmply must expect there to be a value for a given key, so if there isn't one, that's an error that this code can't fix because a basic assumption has failed. In this case it's actually the desired behavior to fall out of the code path and down to some generic handler in case the call fails.

Code should never try to deal with failed basic assumptions.

Except by checking them and throwing well readable exceptions, of course.

Throwing exceptions is not evil but catching them may be. Don't try to fix unexpected errors. Catch exceptions in a few places where you wish to continue some loop or operation, log them, maybe report an unknown error, and that's it.

Catch blocks all over the place are a very bad idea.

Design your APIs in a way that makes it easy to express your intention, i.e. declaring whether you expect a certain case, like key not found, or not. Users of your API can then choose the throwing call for really unexpected cases only.

I guess that the reason that people resent exceptions and go too far by omitting this crucial tool for automation of error handling and better separation of concerns from new languages are bad experiences.

That, and some misunderstanding about what they are actually good for.

Simulating them by doing EVERYTHING through monadic binding makes your code less readable and maintaintable, and you end up without a stack trace, which makes this approach WAY worse.

Functional style error handling is great for expected error cases.

Let exception handling automatically take care of all the rest, that's what it's for :)

yeoman
  • 373
  • 2
  • 6
  • You are saying that basically they are right tool for certain type of jobs. But the problem with exceptions is that they are not review- or reading-friendly. That is when one looks at a code they don't know where an exception can jump out form. So they *should* assume that an exception can interrupt any operation potentially leaving objects in a broken state (like in a severely concurrent code). So for every operation one should make sure it properly cleaned up or undone. This is very difficult if you use liberally “side effects” (which is the default in mainstream languages). – Alexey Nov 28 '19 at 23:48
  • So most people will unlikely exercise the sufficient analysis for such problems, leaving the subtle bugs all over the place. Thus we have functional languages that can provide the environment that make coping with exception’s non-determinism practical, but FLs have other primary mechanisms for error handling. And we have mainstream languages, which are inadequate for coping with the non-determinism, but rely on the exceptions. So the niche for *robust* use of exceptions is very narrow. WDYT? – Alexey Nov 28 '19 at 23:49
  • An alternative approach, like in Rust, allows to focus attention of reviewer/reader/writer to the operations where [recoverable] errors can actually arise. Because the error propagation is explicit. And this makes diligent error handling more practical. – Alexey Nov 29 '19 at 00:11
  • Depending on the kind of code you write, testing is usually supposed to find errors and problems. Code reviews are extremely time consuming and unfriendly to refactoring, as refactoring invalidates prior code reviews when they go into this extreme kind of detail. If you're writing firmware for a mars lander, I agree with your assessment. In most other cases, letting unexpected errors propagate and be reported and letting test pick them up is simply the most efficient way to do bigger projects. – yeoman Feb 08 '20 at 11:40
  • 1
    Plus, Rust, like Swift, allows you to check by throwing for truly unexpected errors. Again, you cannot manually propagate every thinkable or unthinkable broken assumption. Thus, people use tools like swift's fatalError(). The problem is that the language absolutely takes away the possibility of catching those and do some mitigation, like an orderly shutdown. You need to do this out of process, which can force you to change the basic architecture of your entire system. What a horribly invasive decision. Objective C has an honest deal: you can catch but memory will leak. – yeoman May 18 '20 at 05:01
  • I can agree with you that exceptions may be an appropriate for orderly application shutdown (or an equivalent of that, e.g. unloading of a well-isolated module). And I think that in that case application should never catch exceptions, just clean up and propagate the exception. That is only a framework/container or equivalent should actually handle exceptions. – Alexey May 26 '20 at 10:41
  • Exactly. In case you're not using a framework, you'd end up with a single catch statement, or one per module. Expected errors can be nicely handled by implementing some operations, e.g. file system access, in such a module, such abstracting the catch blocks away – yeoman Jun 12 '20 at 11:56
6

One thing I was initially surprised about the Rust language is that it doesn't support catch exceptions. You can throw exceptions, but only the runtime can catch them when a task (think thread, but not always a separate OS thread) dies; if you start a task yourself, then you can ask whether it exited normally or whether it fail!()ed.

As such it is not idiomatic to fail very often. The few cases where it does happen are, for example, in the test harness (which doesn't know what the user code is like), as the top-level of a compiler (most compilers fork instead), or when invoking a callback on user input.

Instead, the common idiom is to use the Result template to explicitly pass up errors that should be handled. This is made significantly easier by the try! macro, which is can be wrapped around any expression that yields a Result and yields the successful arm if there is one, or otherwise returns early from the function.

use std::io::IoResult;
use std::io::File;

fn load_file(name: &Path) -> IoResult<String>
{
    let mut file = try!(File::open(name));
    let s = try!(file.read_to_string());
    return Ok(s);
}

fn main()
{
    print!("{}", load_file(&Path::new("/tmp/hello")).unwrap());
}
o11c
  • 588
  • 2
  • 6
  • 1
    So is it fair to say that this too (like Go's approach) is similar to Swift, which has `assert`, but no `catch`? – orome Oct 04 '14 at 12:43
  • 2
    In Swift, try! means: Yes I know this could fail, but I'm sure it won't, so I'm not handling it, and if it fails then my code is wrong, so please crash in that case. – gnasher729 Aug 29 '16 at 16:46
3

Swift uses the same principles here as Objective-C, just more consequently. In Objective-C, exceptions indicate programming errors. They are not handled except by crash reporting tools. "Exception handling" is done by fixing the code. (There are some ahem exceptions. For example in inter process communications. But that is quite rare and many people never run into it. And Objective-C actually has try / catch / finally / throw, but you rarely use them). Swift just removed the possibility to catch exceptions.

Swift has a feature that looks like exception handling but is just enforced error handling. Historically, Objective-C had a quite pervasive error handling pattern: A method would either return a BOOL (YES for success) or an object reference (nil for failure, not nil for success), and have a parameter "pointer to NSError*" which would be used to store an NSError reference. Swift automagically converts calls to such a method into something looking like exception handling.

In general, Swift functions can easily return alternatives, like a result if a function worked fine and an error if it failed; that makes error handling a lot easier. But the answer to the original question: The Swift designers obviously felt that creating a safe language, and writing successful code in such a language, is easier if the language doesn't have exceptions.

gnasher729
  • 42,090
  • 4
  • 59
  • 119
  • For Swift, this is the correct answer, IMO. Swift has to stay compatible with existing Objective-C system frameworks, so under the hood they don't have traditional exceptions. I wrote a blog post a while ago on how ObjC works for error handling: https://orangejuiceliberationfront.com/why-cocoa-programmers-cant-have-nice-things/ – uliwitness Dec 19 '16 at 17:02
2
 int result;
 if((result = operation_that_can_throw_ioerror()) == IOError)
 {
  handle_the_exception_somehow();
 }
 else
 {
   # we don't want to catch the IOError if it's raised
   result = another_operation_that_can_throw_ioerror();
 }
 result |= something_we_always_need_to_do();
 return result;

In C you would end up with something like the above.

Are there things I can't do in Swift that I could do with exceptions?

No, there isn't anything. You just end up handling result codes instead of exceptions.
Exceptions allow you to reorganize your code so that error handling is separate from your happy path code, but that is about it.

stonemetal
  • 3,371
  • 16
  • 17
  • And, likewise, those calls to `...throw_ioerror()` return errors rather than throwing exceptions? – orome Oct 03 '14 at 19:37
  • 1
    @raxacoricofallapatorius If we are claiming exceptions don't exist then I assume the program follows the usual pattern of returning error codes on failure and 0 on success. – stonemetal Oct 03 '14 at 19:56
  • 2
    @stonemetal Some languages, like Rust and Haskell, use the type system to return something more meaningful than an error code, without adding hidden exit points the way exceptions do. A Rust function, may, for example, return a [`Result`](https://doc.rust-lang.org/std/result/) enum, which can be either an `Ok`, or an `Err`, with `T` being the type you wanted if any, and `E` being a type representing an error. Pattern matching and some particular methods simplify handling of both successes and failures. In short, don't assume lack of exceptions automatically means error codes. – 8bittree May 31 '16 at 15:51
  • Does `result |= something_we_always_need_to_do();` really do it always regardless of what happened perviously? The `|` short curcuits so I thought this only ran the function if the previous thing succeeded? – Jerry Jeremiah Jul 13 '21 at 02:03
  • No, |= doesn't short circuit. | is the bit wise or operator and never short circuits. || is the logical operator that does. Note there is no ||= – stonemetal Sep 14 '21 at 11:29
2

In addition to the Charlie's answer:

These examples of declared exception handling that you see in many manuals and books, look very smart only on very small examples.

Even if you put aside the argument about invalid object state, they always bring about a really huge pain when dealing with a large app.

For example, when you have to deal with IO, using some cryptography, you may have 20 kinds of exceptions that may be thrown out of 50 methods. Imagine the amount of exception handling code you will need. Exception handling will take several times more code than the code itself.

In reality you know when exception can't appear and you just never need to write so much exception handling, so you just use some workarounds to ignore declared exceptions. In my practice, only about 5% of declared exceptions need to be handled in code to have a reliable app.

konmik
  • 129
  • 2
  • 1
    Well, in reality, these exceptions can often be handled in just one place. For example, in a "download data update" function if the SSL fails or the DNS is unresolvable or the web server returns a 404, it doesn't matter, catch it at the top and report the error to the user. – Zan Lynx Aug 29 '16 at 22:56