stakx asked:
This question is intended to apply to any OO programming language that supports exception handling; I am using C# for illustrative purposes only.
Exceptions are usually intended to be raised when an problem happens that the code cannot immediately handle, which is then caught in a catch
clause in a different location (usually an outer stack frame).
My question is this: are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?
This question came up for me because .NET 4's System.IObserver.OnError
method suggests just that: exceptions being passed around as error objects.
Let's look at another scenario, validation. Let's say I am following conventional wisdom and that I am therefore distinguishing between an error object type IValidationError
and a separate exception type ValidationException
that is used to report unexpected errors:
partial interface IValidationError { } abstract partial class ValidationException : System.Exception { public abstract IValidationError[] ValidationErrors { get; } }
(The System.Component.DataAnnotations
namespace does something quite similar.)
These types could be employed as follows:
partial interface IFoo { } // an immutable type partial interface IFooBuilder // mutable counterpart to prepare instances of above type { bool IsValid(out IValidationError[] validationErrors); // true if no validation error occurs IFoo Build(); // throws ValidationException if !IsValid(…) }
Now I am wondering, could I not simplify the above to this:
partial class ValidationError : System.Exception { } // = IValidationError + ValidationException partial interface IFoo { } // (unchanged) partial interface IFooBuilder { bool IsValid(out ValidationError[] validationErrors); IFoo Build(); // may throw ValidationError or sth. like AggregateException }
So what are the advantages and disadvantages of these two differing approaches?
See the original question here.
What’s in an exception?
Mathew Foscarini answers (18 votes):
Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?
If it is never thrown then it's not an exception. It is an object derived
from an Exception class, though it does not follow the behavior. Calling it an Exception is purely semantics at this point, but I see nothing wrong with not throwing it. From my perspective, not throwing an exception is the exact same thing as an inner-function catching it and preventing it from propagating.
Is it valid for a function to return exception objects?
Absolutely. Here is a short list of examples where this may be appropriate:
- An exception factory.
- A context object that reports if there was a previous error as a ready to use exception.
- A function that keeps a previously caught exception.
- A third-party API that creates an exception of an inner type.
Is not throwing it bad?
Throwing an exception is kind of like: "Go to jail. Do not pass Go!" in the board game Monopoly. It tells a compiler to jump over all source code up to the catch without executing any of that source code. It doesn't have anything to do with errors, reporting or stopping bugs. I like to think of throwing an exception as a "super return" statement for a function. It returns execution of the code to somewhere much higher up than the original caller.
The important thing here is to understand that the true value of exceptions is in the try/catch
pattern, and not the instantiation of the exception object. The exception object is just a state message.
In your question you seem to be confusing the usage of these two things: a jumping to a handler of the exception, and the error state the exception represents. Just because you took an error state and wrapped it in an exception does not mean you are following the try/catch
pattern or its benefits.
Related: "If exceptions should only be used in exceptional cases, how do I know if my case is exceptional?"
For example
Philipp answers (42 votes):
Returning exceptions instead of throwing them can make semantical sense when you have a helper-method for analyzing the situation and returning an appropriate exception which is then thrown by the caller (you could call this an "exception factory"). Throwing an exception in this error analyzer function would mean that something went wrong during the analysis itself, while returning an exception means that the kind of error was analyzed successfully.
One possible use-case could be a function which turns HTTP response codes into exceptions:
Exception analyzeHttpError(int errorCode) { if (errorCode < 400) { throw new NotAnErrorException(); } switch (errorCode) { case 403: return new ForbiddenException(); case 404: return new NotFoundException(); case 500: return new InternalServerErrorException(); … default: throw new UnknownHttpErrorCodeException(errorCode); } }
Note that throwing an exception means that the method was used wrong or had an internal error, while returning an exception means that the error code was identified successfully.
Find more answers or leave your own at the original post. See more Q&A like this at Programmers, a site for conceptual programming questions at Stack Exchange. And of course, feel free to login and ask your own question.
24 Reader Comments
Exceptions are "expensive" compared to return codes* and passed in error flags** because they allocate exception data and walk the stack until they find an error handler. The compiler (typically) shouldn't do anything special besides standard optimizations to dictate the code paths taken. Honestly it couldn't in a lot of cases. If it did, loading a library at runtime would ruin its plans. No matter how your code is loaded the stack will be a consistent vector for handler resolution.
Personally I like error flags when I expect there to be problems (i.e. a vendor's API is terrible and I'm always going to check for an error) and exceptions when dealing with the unexpected, like network access issues.
Ex:
*Return code:
int doSomething(){
...
if (somethingBadHappened()){
return 1;
}
...
//Success!
return 0;
}
** Error Flag:
//C/C++
void doSomething(myErrorStruct *foo){
//C#
void doSomething(ref myErrorStruct foo){
...
if (somethingBadHappened()){
foo->error = kSOMETHINGBAD;
foo.error = ErrorPossibilities.SomethingBad;
return;
}
...
//Success!
return;
}
Last edited by WaveRunner on Sat Oct 19, 2013 2:29 pm
Exceptions are "expensive" compared to return codes* and passed in error flags** because they allocate exception data and walk the stack until they find an error handler. The compiler (typically) shouldn't do anything special besides standard optimizations to dictate the code paths taken. Honestly it couldn't in a lot of cases. If it did, loading a library at runtime would ruin its plans. No matter how your code is loaded the stack will be a consistent vector for handler resolution.
Depends on the language and the JIT really. Yes you're generally right, but fun fact: Some early benchmark code in Java used ArrayOutOfBoundsExceptions to iterate through arrays and other funny things.
Which - you guessed it - leads to some rather interesting optimizations for this kind of thing in HotSpot..
If you embed a try/catch in every function, especially if you nest them, you are complicating the code unnecessarily. What's the point of having 100 helicopters hovering just in case a person gets a heart attack. Keep exceptions for truly exceptional circumstances. Anywhere else you're better off simply returning an error code or an exception, whichever is more convenient, and deal with as part of the normal flow.
Exceptions are "expensive" compared to return codes* and passed in error flags** because they allocate exception data and walk the stack until they find an error handler. The compiler (typically) shouldn't do anything special besides standard optimizations to dictate the code paths taken. Honestly it couldn't in a lot of cases. If it did, loading a library at runtime would ruin its plans. No matter how your code is loaded the stack will be a consistent vector for handler resolution.
Personally I like error flags when I expect there to be problems (i.e. a vendor's API is terrible and I'm always going to check for an error) and exceptions when dealing with the unexpected, like network access issues.
Ex:
*Return code:
int doSomething(){
...
if (somethingBadHappened()){
return 1;
}
...
//Success!
return 0;
}
** Error Flag:
//C/C++
void doSomething(myErrorStruct *foo){
//C#
void doSomething(ref myErrorStruct foo){
...
if (somethingBadHappened()){
foo->error = kSOMETHINGBAD;
foo.error = ErrorPossibilities.SomethingBad;
return;
}
...
//Success!
return;
}
I don't know how this guy got voted down. Exceptions causes a context switch, which costs thousands of clock cycles just in processing, plus lost cycles due to cache thrashing.
Exceptions should not be used except for exceptional situations that rarely occur.
It might not be appropriate in some languages however. For example in Objective-C there is an "Exception" class and an "Error" class. They're both pretty much the same, except an "Error" cannot be thrown, rather it is intended to be used the way exceptions are described in this question.
I think it's far easier to think of exceptions as exceptions, instead of some kind of crazy metaphor. They're nowhere near as complicated as a heart attack and bunch of helicopters.
A metaphor should make things simpler, not even more complicated.
Exceptions are "expensive" compared to return codes* and passed in error flags** because they allocate exception data and walk the stack until they find an error handler. The compiler (typically) shouldn't do anything special besides standard optimizations to dictate the code paths taken. Honestly it couldn't in a lot of cases. If it did, loading a library at runtime would ruin its plans. No matter how your code is loaded the stack will be a consistent vector for handler resolution.
Personally I like error flags when I expect there to be problems (i.e. a vendor's API is terrible and I'm always going to check for an error) and exceptions when dealing with the unexpected, like network access issues.
Ex:
*Return code:
int doSomething(){
...
if (somethingBadHappened()){
return 1;
}
...
//Success!
return 0;
}
** Error Flag:
//C/C++
void doSomething(myErrorStruct *foo){
//C#
void doSomething(ref myErrorStruct foo){
...
if (somethingBadHappened()){
foo->error = kSOMETHINGBAD;
foo.error = ErrorPossibilities.SomethingBad;
return;
}
...
//Success!
return;
}
I don't know how this guy got voted down. Exceptions causes a context switch, which costs thousands of clock cycles just in processing, plus lost cycles due to cache thrashing.
Exceptions should not be used except for exceptional situations that rarely occur.
I didn't vote him down, but I don't think the issue he's raising applies to this discussion at all.
Exceptions only cause a context switch when you throw them. An exception that never gets thrown does not cost any clock cycles and is a clean way to describe an error condition.
Also, premature optimisation is the root of all evil. Do not worry about the speed of an exception unless you have first demonstrated that your use of them is slow.
The real reason not to use exceptions has nothing to do with performance (in 99% of cases) and everything to do with the fact it completely breaks the normal flow of code, allowing you to jump multiple levels up the execution stack instead of going up the stack one at a time giving each item a chance to clean up along the way.
And I think when you see "abstract partial class" that's an indication that overengineering is creeping in. To solve complexity, make it more complex?
No.
While *he* was absolutely right, your own claim is completely bogus. Why do you think you'd need a context switch to throw/catch an exception?
Exactly the same thing you'd do if you would get an error code back?
As has been pointed out, throwing an exception is an expensive process and therefore should not be used in cases where there is a good chance that he exception will be thrown often. For example, validating user input could use the exception mechanism, but only if the outcome was to stop processing further input. But processing data within a loop should not throw exceptions unless it causes the loop to be exited.
As for the original question, objects representing exceptional outcomes are comparable to return codes and should be used in similar situations. But they should not be confused with exception objects, and should probably not be termed such (e.g., throw Exception objects, return Result objects).
Typically if a method that I'm writing can fail in a non-exceptional way I try to return a null value instead of the expected type and test for null where the method is called to see what to do instead.
I also agree that passing around exception objects isn't exception handling, rather it is error handling using an exception object as a container for the error. Personally I think that passing around of exception objects should only be done for factory methods or logging methods. In all other situations I can think of you're either forcing the code to use object return types where another type should be used (thus adding unnecessary and expensive type checking to all callers). Or you should be throwing the exception and having finally blocks for any cleanup you may need to do on the way.
When coding asynchronous code you sort of need to provide the callback some way of knowing that shit went wrong. For example node.js cb(err, res) pattern. So yes.
That said you can use something like the Exception Monad and pipeline commands so that the pipeline halts on an exception. This is cheaper than throwing while still using exceptions to carry the error report. Best of both worlds. It does require a more functional mindset though.
An encoding issue on Ars? I am disappointed.
(The original is ellipses: …)
More important, I think, is the cost in code complexity. Returning an error is a trivial thing to understand: the caller gets it. Throwing an exception, OTOH, can take execution any number of levels up the call hierarchy. The kind of programmer who being given a hammer sees all things as nails should not be let near exceptions.
As for the question itself, if you take it literally the answer is 'obviously'. Not just factories, but even constructors are technically returning exception objects. However, a special case is whether it's ever a good idea to return an exception instead of throwing to indicate an exception/error in the function. That seems likely to confuse readers, better to return some other error object instead.
I don't think it's that complicated. If you come across something after which it's not possible to continue executing the rest of your code, whatever it is, throw an exception. Then make sure you have a centralized handler for all exceptions further up the callstack, that usually just logs it. And if there's exceptions you want to treat differently, have a handler further down the callstack for those.
And you are now relying on the caller to actually do something with that return code. What if he ignores the return value and simply continues on? That might be a valid decision, but with exceptions, that becomes a conscious choice: you have to catch the exception, a conscious act. With returning codes or errors, you have now built in a dependency on the infallibility of the developer.
I really, really, really don't see the value in returning errors as part of any API. It might be a valid choice within a very narrow scope (like the exception factory example) but never part of the public or even internal interface of your code if you write in a language that supports exceptions.
Exactly. That's the point. Your program has reached a point at which execution can no longer continue, so we have to unwind the stack back to where it's possible to continue (so where there's an exception handler) or if that's not possible, terminate the program. The thrower of the exception generally doesn't even have to concern himself with where it's caught, he just encountered something that made it impossible for him to continue executing the rest of the code he wrote, so he needs to bail. Where it gets caught, should generally not concern him.
EDIT: In fact, as I wrote the above, I realized that returning errors instead of throwing exceptions is a recurring problem in product I work on. I have a webservice and if an error occurs, the response to the webservice contains an error object. There's no real choice, you can't throw an exception in that scenario. But I am reliant on the caller of the webservice to check if the response contains an exception (response.Exception != null) and 9 out of 10 times, they don't. The right thing for them to do is in the part that actually performs the HTTP request to my webservice, add a centralized check for the existence of the exception object and if it exists, throw an exception.
Actually, it depends on the language you are using...
If you are using C#, you don't have to catch anything. Uncaught exceptions will just crash your application.
If you are using Java, then yes, the compiler will throw an error if you don't catch exceptions.
So if using C#, if you don't catch exceptions, you're in the same position if you returned NULL, and didn't check the value, and tried to dereference it.. Both will crash your app.
This brings up the other problem with exceptions... In Java, you have to declare what your function throws. So when you write your app, and you use somebody elses code, you know what to expect, because you can see what it can throw...
With C#, you don't have that... So you have to read the documentation, and hope the developer stuck to the documentation, otherwise you don't have the foggiest idea what could be thrown.
That's why I think a lot of C# guys, just return error codes, because it's status quo, and is reminiscent of returning an HRESULT.
Actually, it depends on the language you are using...
If you are using C#, you don't have to catch anything. Uncaught exceptions will just crash your application.
If you are using Java, then yes, the compiler will throw an error if you don't catch exceptions.
So if using C#, if you don't catch exceptions, you're in the same position if you returned NULL, and didn't check the value, and tried to dereference it.. Both will crash your app.
That's not what I mean though. If you throw an exception, the stack will unwind to the nearest applicable handler, meaning that you can't possibly erroneously continue. Usually you'll have a central handler, but if you don't, yeah the program crashes. But that's not important. What's important that you weren't allowed to continue executing the rest of your code, unless you made the conscious decision to catch it. There is no possible way to accidentally ignore a failure of something. With an error code, those guarantees don't exist. Say I called a method to save something to my database (say something important like a payment), the database save failed because it timed out. Then you return an error code/object which I happily completely ignore and then I'll display a nice message to my customer saying "payment succeeded. Thank you for your purchase!" even though it didn't succeed at all. With exceptions, that simply can't happen. If the exception crashes the program/request, that's fine, it means you weren't able to do anything meaningful with the exception, the only logical thing is to crash. Crashing or not crashing is not important, that you are not allowed to continue by accident is the important bit.
Java's checked exceptions are a nightmare, though.
As a C# guy, I'll never ever merge a pull request that returns an error code unless there's a damn good reason for it.
And honestly, I can't think of any popular .NET library that works with error codes. There's FluentValidation that returns a ValidationResult, but a validation error does not necessarily result in an exception. That's up to the caller of the library.
There are lots of libraries that return enumerations for error codes. Tho most of the ones I've seen are also wrappers for C/C++ libraries. The ones that weren't wrappers, usually had an enumeration as part of the async handlers to propagate errors that were encountered.
As far as .NET libraries... If you look at the I/O libraries, particularly the async ones... Some of them in the AsyncResult handler, the EndRead method will succeed and return 0 on a graceful close, while some will throw an IOException on the EndRead method call on a graceful close.
Last edited by a_v_s on Sun Oct 20, 2013 1:44 am
Sure there is... I've seen it frequently with people that forget to check their code for re-entrancy. I've seen people catch an exception that they were expecting to be thrown by the method they were calling, without realizing that method they called made a callback into their code somewhere, which happened to call some API that could throw that same exception but they forgot to put an exception handler for it, and it ends up getting caught somewhere else, where the context was different so the failure path was wrong, and the error ends up getting dropped.
For example, let say someone calls some method, and tries to catch an IOException. For the method they are calling, this exception means there was a graceful close, so they gracefully shut down, and continue... However, they didn't know that this method happened to make a callback into their code somewhere, where it was doing something else, and made it's own IO calls for something, but forgot to catch any IOExceptions, and it ends up propagating to the other try/catch block, where in this context, an IOException means a graceful close, even tho in the context it was actually thrown, it was not a graceful close, so the app treats it as a graceful close, and continues, even tho this was the wrong course of action.
Granted, this is also sloppy design, but I'm not trying to argue this is good design, just refuting your point that it's impossible to accidentally ignore a failure.
Sure there is... I've seen it frequently with people that forget to check their code for re-entrancy. I've seen people catch an exception that they were expecting to be thrown by the method they were calling, without realizing that method they called made a callback into their code somewhere, which happened to call some API that could throw that same exception but they forgot to put an exception handler for it, and it ends up getting caught somewhere else, where the context was different so the failure path was wrong, and the error ends up getting dropped.
For example, let say someone calls some method, and tries to catch an IOException. For the method they are calling, this exception means there was a graceful close, so they gracefully shut down, and continue... However, they didn't know that this method happened to make a callback into their code somewhere, where it was doing something else, and made it's own IO calls for something, but forgot to catch any IOExceptions, and it ends up propagating to the other try/catch block, where in this context, an IOException means a graceful close, even tho in the context it was actually thrown, it was not a graceful close, so the app treats it as a graceful close, and continues, even tho this was the wrong course of action.
Granted, this is also sloppy design, but I'm not trying to argue this is good design, just refuting your point that it's impossible to accidentally ignore a failure.
This is why proper logging of exceptions is so important as the logging (in dotnet anyway) should show where the exception occurred. IMO exceptions should always be logged even if debug logging has been switched off. That way you can review the logs for frequent exceptions and try to trace the causes.
And you are now relying on the caller to actually do something with that return code. What if he ignores the return value and simply continues on? That might be a valid decision, but with exceptions, that becomes a conscious choice: you have to catch the exception, a conscious act. With returning codes or errors, you have now built in a dependency on the infallibility of the developer.
IMO it's the same with both. You don't need to actively do anything to ignore the return value or the exception. The only difference is what happens if you do that. When ignoring return values you continue on as if nothing is wrong, which may or may not work. When ignoring exceptions your app by default blows up. Which is better depends...
(It's different if you are developing a library: any exceptions you ignore can still be caught by the caller, while the caller can do nothing about the return values you ignore.)
Exactly. That's the point. Your program has reached a point at which execution can no longer continue, so we have to unwind the stack back to where it's possible to continue (so where there's an exception handler) or if that's not possible, terminate the program. The thrower of the exception generally doesn't even have to concern himself with where it's caught, he just encountered something that made it impossible for him to continue executing the rest of the code he wrote, so he needs to bail. Where it gets caught, should generally not concern him.
Oh, I'm not saying they aren't a good tool. They are often easy to use even. However, they do make it more difficult for a reader of the code to see what happens when an exception is thrown: finding the callers of a function is usually trivial, recursively finding all the places up the call chain where the exception might be caught is not.
You must login or create an account to comment.