47

In order to handle several possible errors that shouldn't halt execution, I have an error variable that clients can check and use to throw exceptions. Is this an Anti-Pattern? Is there a better way to handle this? For an example of this in action you can see PHP's mysqli API. Assume that visibility problems (accessors, public and private scope, is the variable in a class or global?) are handled correctly.

Cloudy
  • 703

11 Answers11

69

If a language inherently supports exceptions, then it is preferred to throw exceptions and the clients can catch the exception if they do not want it to result in a failure. In fact, the clients of your code expect exceptions and will run into many bugs because they will not be checking the return values.

There are quite a few advantages to using exceptions if you have a choice.

Messages

Exceptions contain user readable error messages which can be used by the developers for debugging or even displayed to the users if so desired. If the consuming code cannot handle the exception, it can always log it so the developers can go through the logs without having to stop at every other trace to figure out what was the return value and map it in a table to figure out what was the actual exception.

With return values, there is no additional information can be easily provided. Some languages will support making method calls to get the last error message, so this concern is allayed a bit, but that requires the caller to make extra calls and sometimes will require access to a 'special object' that carries this information.

In the case of exception messages, I provide as much context as possible, such as:

A policy of name "foo" could not be retrieved for user "bar", which was referenced in user's profile.

Compare this to a return code -85. Which one would you prefer?

Call stacks

Exceptions usually also have detailed call stacks which help debug code faster and quicker, and can also be logged by the calling code if so desired. This allows the developers to pinpoint the issue usually to the exact line, and thus is very powerful. Once again, compare this to a log file with return values (such as a -85, 101, 0, etc.), which one would you prefer?

Fail fast biased approach

If a method is called somewhere that fails, it will throw an exception. The calling code has to either suppress the exception explicitly or it will fail. I have found this to be actually amazing because during development and testing (and even in production) the code fails quickly, forcing the developers to fix it. In the case of return values, if a check for a return value is missed, the error is silently ignored and the bug surfaces somewhere unexpected, usually with a much higher cost to debug and fix.

Wrapping and Unwrapping Exceptions

Exceptions can be wrapped inside other exceptions and then unwrapped if needed. For example, your code might throw ArgumentNullException which the calling code might wrap inside a UnableToRetrievePolicyException because that operation had failed in the calling code. While the user might be shown a message similar to the example I provided above, some diagnostic code might unwrap the exception and find that an ArgumentNullException had caused the issue, which means it is a coding error in your consumer's code. This could then fire an alert so the developer can fix the code. Such advanced scenarios are not easy to implement with the return values.

Simplicity of code

This one is a bit harder to explain, but I learnt through this coding both with return values as well as exceptions. The code that was written using return values would usually make a call and then have a series of checks on what the return value was. In some cases, it would make call to another method, and now will have another series of checks for the return values from that method. With exceptions, the exception handling is far simpler in most if not all cases. You have a try/catch/finally blocks, with the runtime trying its best to execute the code in the finally blocks for clean-up. Even nested try/catch/finally blocks are relatively easier to follow through and maintain than nested if/else and associated return values from multiple methods.

Conclusion

If the platform you are using supports exceptions (esp. such as Java or .NET), then you should definitely assume that there is no other way except to throw exceptions because these platforms have guidelines to throw exceptions, and your clients are going to expect so. If I were using your library, I will not bother to check the return values because I expect exceptions to be thrown, that's how the world in these platforms is.

However, if it were C++, then it would be a bit more challenging to determine because a large codebase already exists with return codes, and a large number of developers are tuned to return values as opposed to exceptions (e.g. Windows is rife with HRESULTs). Furthermore, in many applications, it can be a performance issue too (or at least perceived to be).

Omer Iqbal
  • 3,264
22

There are multiple ways to signal an error:

  • an error variable to check: C, Go, ...
  • an exception: Java, C#, ...
  • a "condition" handler: Lisp (only ?), ...
  • a polymorphic return: Haskell, ML, Rust, ...

The problem of the error variable is that it is easy to forget to check.

The problem of exceptions is that is creates hidden paths of executions, and, although try/catch is easy to write, ensuring a proper recovery in the catch clause is really difficult to pull off (no support from type systems/compilers).

The problem of condition handlers is that they do not compose well: if you have dynamic code execution (virtual functions), then it is impossible to predict which conditions should be handled. Furthermore, if the same condition can be raised in several spots, there is no telling that a uniform solution can be applied each time, and it quickly becomes messy.

Polymorphic returns (Either a b in Haskell) are my favorite solution so far:

  • explicit: no hidden path of execution
  • explicit: fully documented in function type signature (no surprises)
  • hard to ignore: you have to pattern match to get the desired result out, and handle the error case

The only issue is that they may potentially lead to excessive checking; the languages that use them have idioms to chain the calls of functions that use them, but it may still require a bit more typing/clutter. In Haskell this would be monads; however, this is far scarier than it sounds, see Railway Oriented Programming.

Fuhrmanator
  • 1,475
Matthieu M.
  • 15,214
21

Error variables are a relic from languages like C, where exceptions were not available. Today, you should avoid them except when you are writing a library which is potentially used from a C program (or a similar language without exception handling).

Of course, if you have a type of error which could be better classified as "warning" (= your library can deliver a valid result and the caller can ignore the warning if he thinks it is not important), then a status indicator in form of a variable can make sense even in languages with exceptions. But beware. Callers of the library tend to ignore such warnings even if they should not. So think twice before introducing such a construct into your lib.

Fuhrmanator
  • 1,475
Doc Brown
  • 218,378
13

I think it's awful. I'm currently refactoring a Java app that uses return values instead of exceptions. Although you may not at all be working with Java, I think this applies nonetheless.

You end up with code like this:

String result = x.doActionA();
if (result != null) {
  throw new Exception(result);
}
result = x.doActionB();
if (result != null) {
  throw new Exception(result);
}

Or this:

if (!x.doActionA()) {
  throw new Exception(x.getError());
}
if (!x.doActionB()) {
  throw new Exception(x.getError());
}

I'd much rather have the actions throw exceptions themselves, so you end up with something like:

x.doActionA();
x.doActionB();

You can wrap that in a try-catch, and get the message from the exception, or you can choose to ignore the exception, for example when you're deleting something that may already be gone. It also preserves your stack trace, if you have one. The methods themselves become easier, too. Instead of handling the exceptions themselves, they just throw what went wrong.

Current (horrible) code:

private String doActionA() {
  try {
    someOperationThatCanGoWrong1();
    someOperationThatCanGoWrong2();
    someOperationThatCanGoWrong3();
    return null;
  } catch(Exception e) {
    return "Something went wrong!";
  }
}

New and improved:

private void doActionA() throws Exception {
  someOperationThatCanGoWrong1();
  someOperationThatCanGoWrong2();
  someOperationThatCanGoWrong3();
}

Strack trace is preserved and the message is available in the exception, rather than the useless "Something went wrong!".

You can, of course, supply better error messages, and you should. But this post is here because the current code I'm working on is a pain, and you shouldn't do the same.

mrjink
  • 728
  • 5
  • 12
5

There is often nothing wrong with using this pattern or that pattern, as long as you use the pattern that everyone else uses. In Objective-C development, the much preferred pattern is to pass a pointer where the method that is called can deposit an NSError object. Exceptions are reserved for programming errors and lead to a crash (unless you have Java or .NET programmers writing their first iPhone app). And this works quite well.

gnasher729
  • 49,096
5

"In order to handle several possible errors happening, that shouldn't halt execution,"

If you mean that the errors should not halt execution of the current function, but should be reported to the caller in some way - then you have a few options that have not really been mentioned. This case is really more a warning than an error. Throwing/Returning is not an option as that ends the current function. A single error message paramter or return only allows for at most one of these errors to occur.

Two patterns that I've used are:

  • An error/warning collection, either passed in or kept as a member variable. Which you append stuff to and just keep processing. I personally don't really like this approach as I feel it disempowers the caller.

  • Pass in an error/warning handler object (or set it as a member variable). And each error calls a member function of the handler. This way the caller can decide what to do with such non-terminating errors.

What you pass to these collections/handlers should contain enough context for the error to be handled "correctly" - A string is usually too little, passing it some instance of Exception is often sensible - but sometimes frowned upon (as an abuse of Exceptions).

Typicial code using an error handler might look like this

class MyFunClass {
  public interface ErrorHandler {
     void onError(Exception e);
     void onWarning(Exception e);
  }

  ErrorHandler eh;

  public void canFail(int i) {
     if(i==0) {
        if(eh!=null) eh.onWarning(new Exception("canFail shouldn't be called with i=0"));
     }
     if(i==1) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=1 is fatal");
        throw new RuntimeException("canFail called with i=2");
     }
     if(i==2) {
        if(eh!=null) eh.onError(new Exception("canFail called with i=2 is an error, but not fatal"));
     }
  }
}
4

The question is already answered, but I can't help myself.

You can't really expect Exception to provide a solution for all use cases. Hammer anyone?

There are cases where Exceptions are not the end all and be all, for example, if a method receives a request and is responsible for validating all the fields passed, and not only the first one then you have to think that it should be possible to indicate the cause of the error for more than one fields. It should be possible to also indicate if the nature of the validation prevents the user from going further or not. An example of that would be a not strong password. You could show a message to the user indicating that the entered password is not very strong, but that it is strong enough.

You could argue that all of these validations could be thrown as an exception at the end of the validation module, but they would be error codes in anything but in the name.

So the lesson here is: Exceptions have their places, as do error codes. Chose wisely.

4

There are use cases were error codes are preferable to exceptions.

If your code can continue despite the error, but it needs reporting, then an exception is a poor choice because exceptions terminate the flow. For example, if you're reading in a data file and discover it contains some non-terminal piece of bad data it might be better to read in the rest of the file and report the error rather than fail outright.

The other answers have covered why exceptions should be preferred to error codes in general.

2

There's definitely nothing wrong with not using exceptions when exceptions are not a good fit.

When the code execution should not be interrupted (e.g. acting on user input that may contain multiple errors, like a program to compile or a form to process), I find that collecting errors in error variables like has_errors and error_messages is indeed far more elegant design than throwing an exception at the first error. It allows to find all errors in user input without forcing the user to resubmit unnecessarily.

ikh
  • 938
1

In some dynamic programming languages you can use both error values and exception handling. This is done by returning unthrown exception object in place of ordinary return value, which can be checked like an error value, but it throws an exception if it isn't checked.

In Perl 6 it is done via fail, which if withing no fatal; scope returns a special unthrown exception Failure object.

In Perl 5 you can use Contextual::Return you can do this with return FAIL.

-1

Unless there is something very specific, I think having an error variable for validation is a bad idea. The purpose seems to be about saving the time spent on validation (you can just return the variable value)

But then if you change anything, you have to recompute that value anyway. I can't say more about halting and Exception throwing though.

EDIT: I didn't realize this is a question of software paradigm instead of a specific case.

Let me further clarify my points in a particular case of mine in which my answer would make sense

  1. I have a collections of entity objects
  2. I have procedural style web services which work with these entity objects

There are two kinds of errors:

  1. The errors when processing happens in the service layer
  2. The errors because there are inconsistencies in the entity objects

In the service layer, there is no choice but using Result object as wrapper which is the equivalence of error variable. Simulating an exception via service call on protocol like http is possible, but is definitely not a good thing to do. I am not talking about this kind of error and didn't think this is the kind of error being asked in this question.

I was thinking about the second kind of error. And my answer is about this second kind of errors. In the entity objects, there are choices for us, some of them are

  • using a validation variable
  • throw an exception immediately when a field is set incorrectly from the setter

Using validation variable is the same as having a single validation method for each entity object. In particular, the user can set the value in a way that keep the setter as purely setter, no side effect (this is often a good practice) or one can incorporate validation into each setter and then save the result into a validation variable. The advantage of this would be to save time, the validation result is cached into the validation variable so that when the user calls validation() multiple time, there is no need to do multiple validation.

The best thing to do in this case is using a single validation method without even using any validation to cache validation error. This helps keep the setter as just setter.

InformedA
  • 3,031