Related
what can't be done with if else clause, and can be done with exception handling ?
In other words where do we actually need to use exception handling and mere if else won't serve the purpose.
Is exception handling just a glorified way of showing errors ?
In the earliest C++ implementations, exceptions were compiled into equivalent if/else constructs, more or less. And they were slow as molasses, to the point where you can still find programming guides that recommend against exceptions on the grounds that "they are slow".
So it is not a question of "can" or "cannot". It is a question of readability and performance.
Littering every line of your code with error checks makes it harder for a human to follow the non-exceptional (i.e. common) case. And as every good programmer knows, the primary audience for your code is human readers.
As for performance, if/else is slower than a modern exception implementation. Modern implementations incur literally zero overhead except when an exception is actually thrown. If your exceptions truly represent "exceptional" cases, this can be a significant performance difference.
Some languages (for example C++) don't allow return values in certain cases. For example, in C++ constructors don't have return values (even void), so the only way to signal an error in a constructor is to throw an exception.
Look, there isn't anything you can do with exceptions you can't do with hand-coded 8086 assembler. (Turing complete!) Except that hand-coded assembler isn't a very good tool for a 100K lines-of-code project unless you are the best coder in the Milky Way, if then. Experience shows a number of situations where the idiom of exception handling yields the most robust code written by ordinary humans. Sharptooth's example of ctors is good. So are errors that need to propagate up the call stack. Edit: And how many people actually check that malloc didn't fail every single time? Better to throw an OutOfMemoryException.
Exceptions are just what they are named for: Exceptional events or situations which break the current flow.
You should use exceptions to indicate that something nasty and very exceptional has happened. Let's say you have a class which reads your configuration file. Exceptional situations could be:
The file cannot be found.
The file exists, but cannot be read.
The file was deleted in the middle of
a read operation.
You could handle all of these with if-else blocks but it is much simpler to do with exceptions.
Of course, you can write code that does not use exceptions. However, if you do, you would have make sure that any function that could issue an error is handled properly.
For me, the biggest advantage of having exceptions is that I can write straight code that simply assumes that all functions succeeds, knowing that upper layers will take care of reporting errors.
Concretely, take the following fictitious function to work with text in an editor:
do-stuff:
backward-line 10
x = point
search-for "FOO"
return buffer-substring x point
Both "backward-line" and "search-for" could fail. If I would have to handle errors myself, I would have to check them. In addition, I would have to invent a side-channel to report to my caller that an error occurred. As I said, it can be done, but it would be a lot messier.
Consider a following real-life example. You have a complex recursive function and need to get out of all the calls currently in progress on some condition.
void complexFunction()
{
//do stuff, then
if( timeToBailOut() ) {
// what? how would you get out of all instances at once?
}
}
You would need to write those if statements very carefully, perhaps introduce a special return value, check for that value at all times, that would complicate you code greatly. You can easily achieve that behavior with exceptions.
if( timeToBailOut() ) {
throw BailOutException();
}
There is nothing that can be done with exception handling that can't be done without it, however, some situations would be a pain without exception handling.
Example: a() calls b() and b() calls c(). An error might occur in c() that has to be handled in a(). Without exception handling you would have to deal with it by using sentinel return values in b() and c(), and extra checking code in b(). You can imagine how much more code you would need to write if the error has to be passed through more levels from what caused it to what needs to handle it.
This is a short version of my answer to a different question that was asked here earlier and migrated.
I've just read this post about Panic/Recover in Go and I'm not clear on how this differs from try/catch in other mainstream languages.
Panic/Recover are function scoped. It's like saying that you're only allowed one try/catch block in each function and the try has to cover the whole function. This makes it really annoying to use Panic/Recover in the same way that java/python/c# etc. use exceptions. This is intentional.
This also encourages people to use Panic/Recover in the way that it was designed to be used. You're supposed to recover() from a panic() and then return an error value to the caller.
I keep looking at this question trying to think of the best way to answer it. It's easiest to just point to the idiomatic uses for panic/recover as opposed to try/catch &| exceptions in other languages, or the concepts behind those idioms (which can be basically summed up as "exceptions should only occur in truly exceptional circumstances")
But as to what the actual difference is between them? I'll try to summarize as best I can.
One of the main differences compared to try/catch blocks is the way control flows. In a typical try/catch scenario, the code after the catch block will run unless it propagates the error. This is not so with panic/recover. A panic aborts the current function and begins to unwind the stack, running deferred functions (the only place recover does anything) as it encounters them.
Really I'd take that even further: panic/recover is almost nothing like try/catch in the sense that try and catch are (or at least act like) control structures, and panic/recover are not.
This really stems out of the fact that recover is built around the defer mechanism, which (as far as I can tell) is a fairly unique concept in Go.
There are certainly more, which I'll add if I can actuate my thoughts a bit better.
I think we all agree that panic is throw, recover is catch, and defer is finally.
The big difference seems that recover goes inside defer. Going back to traditional terms, it lets you decide exactly at which point of your finally you want to bother catching anything, or not at all.
defer is a mechanism not only for handling errors but also for doing a comfortable and controlled cleanup. Now panic works like raise() in other languages. With the help of the function recover() you've got the chance to catch this panic while it goes up the call stack. This way it's almost similar to try/catch. But while the latter works on blocks panic/recover works on function level.
Rob Pike about the reason for this solution: "We don't want to encourage the conflation of errors and exceptions that occur in languages such as Java.". Instead of having a large number of different exceptions with an even larger number of usages one should do everything to avoid runtime errors, deliver proper error return values after determination and use panic/recover only if there's no other way.
I think the Panic is the same as throw,Recover is the same as catch. The difference is at defer. At the begin, I think defer is the same as finally,but later, I find defer is more flexible than finally. defer can be place at any scope of your function and remember the value of parameter at that moment and also can change the returned return value,the the panic can be at any where after defer. but because the missing of block of try, we can't process the "exception" unless the whole function returned. I don't think this is a disadvantage,maybe GO want to make your method only do one thing, any exception should make this thing can't go on.
and since panic must after defer, it make you must process its "exception" before use it.
this is just understanding of myself .
"go" is unlike other languages a concurrent language, meaning that parts of the program runs in parallel. This means that if the designers want to have a similar mechanism as catch/throw , the mechanism has to meticulously redefined in this context. That explains the differences between the two mechanisms.
This one has been puzzling my for some time now.
Let's imagine a class which represents a resource, and in order to be able to use this resource one needs to first call the 'Open' method on it, or an InvalidOperationException will be thrown.
Should my code also check whether someone tries to open an already open resource, or close an already closed one?
Should code prevent a logically invalid invocation even when no harm would be done?
I think that programming this way would help writing better code at the other side, but I feel that I might be taking too much responsibility and affect reusability.
What do you guys think?
Edit:
I don't think this could be called defensive programming because it won't let a possible bad use to slip either, and another InvalidOperationException will be thrown.
This is called defensive programming. That's a good programming practice because you ensure that your application doesn't crash on misbehaviour.
That some method should be called first before another method is called, is not a good programming practice. It add's a lot of complexity, which is better handled by the class itself.
This is called sequential coupling. This wikipedia article says that it depends on the context if it's a bad practice, but it shouldn't crash when handled improperly. Sometimes it's necessary to throw an exception to make things clear.
This really depends on what the class actually does. In some cases failing silently is a good idea (eg, you want your DVD player to continue working, not show an error message if it opens the DVD tray that is already open) and in other cases you want as much information as possible (eg, if an airplane tries to close a door that is reportedly already closed, then something is wrong and the pilot should be alerted).
In most cases throwing an error when a logically invalid action is performed is useful for developers, so implementing those exceptions depends on who will use the code. If it is used internally for one application, then it's not vital. But if it is used by many different projects or developers, then I would look into it.
If your example is really the case, then the Open functionality should probably be invoked by the class's constructor.
If you consider the C++ iostream library (which is very widely used and considered quite a good example) you can call any operation on a stream class, whether it is open or not. The called function will simply return a failure indicator of some sort if the operation could not be performed. The functions must of course test the stream state in order to do this.
What you must not do is allow your programs to silently accept any old input as parameters. For example, this would be a broken implementation of strlen()
int strlen( const char * s )
{
if ( s == 0 )
{
return 0; // bad
}
else
{
// calculate length not shown
}
}
as it fields bad inputs without causing a fuss - it should instead throw an exception or use an assert(), depending on your exact development philosophy.
There's no substitute for taste, talent and experience in figuring out exactly how many safety checks should be in your code for best cost/benefit ratio for your organization.
A good quality APIs are expected to be fool-proof, and to guide the user with proper amount of warnings.
Sometimes, safety precautions may impair performance. Performance is one of the most counter-intuitive things in programming. Optimize with care, only when performance really matters.
If this is part of a public SDK that you're releasing to the wild, then the exposed API calls should have strong validation. It will help your 'users' (who are developers) and ensure you aren't stuck supporting usage you never intended to support.
Otherwise, I would not add such checks. I think they make the code harder to read, and these checks are rarely tested. In the past I would add a lot of code like this to make sure my code doesn't do the wrong thing. Now I write unit tests to verify my code does the right thing. The difference? I think tests are more maintainable, more readable, and they don't clutter your production code.
In the case of opening a file that is already open, it depends on knowing the effect of the request, will it reset the current read location for example.
In the case of closing a file that is already closed, think of it as a request for the file to be put in a known state. The code doesn't have to do anything but the desired state is acheived so the code can return a success condition. This is not true if there is some sort of file buffering that needs to be taken care of or maybe an interlinked resource to coordinate, like a modem/serial port or a printer/spooler.
Step back and think of the problem in terms of the desired outcome including any side-effects.
We once put a 'logout' link on an app menu that was displayed regardless of your login status. Why? Because it only took a simple (and very short) method to handle returning you to the login screen from the login screen and saved a large number of checks to handled tracking the login status just so the 'logout' menu-item was displayed only when you were logged in.
logical invalid invocations should always be reported to the user in debug mode..
When compiled in release mode, your code should not throw any unneeded exceptions or do anything else which could endanger the whole application.
Personally i prefer having some kind of logfile, and logging such logically invalid invocations surely will do no harm (at least when performance is not important)
A lot of developers say only throw exceptions in truly exceptional circumstances. One of these would be if an external hard drive I want to write to is not switched on (therefore not a connected/registered drive). However, there are some situations which are difficult to work out whether they are truly exceptional or not.
For example, entering a string for a folder path but it is not found. In this case, if there is any input which cannot be found (like a name in a collection which is not found), is it best to just return an error message and some action?
E.G.
public void Find(string name)
{
if(Names.contains(name)
{
string s = Names.get(name);
}
if(!Names.contains(string name)
{
throw new ???Exception;
}
}
Or do something like display a popup and handle the situation gracefully?
Is it wise to throw an exception in an else or if statement? Looking at a list of code smells regarding exception handling would do me a lot of favours.
Generally, it works like this:
If you can handle the situation without any interruptions, do so. (File doesn't exist, but its input isn't essential to continuing operation [preferences, optional configuration, etc])
If you need user intervention, ask them. (File doesn't exist, but you need it to continue operating)
If it's a problem the user can't fix (out-of-memory, failed hardware, etc), then throw an exception.
Each place has their own standard for the details, but I find the above to work generally.
if your code can recover from the exception, do so
if it would be acceptable to require clients to check a return value for minor expected exceptions, do so - this is a judgement call, see below
in all other cases, throw an exception (or don't catch a called method's exception)
for the second case, minor and expected are highly context-sensitive
IMHO don't use exceptions for control-flow, but in all other cases you are probably safer throwing them
note that some 'optimization' advice tells you to check for conditions instead of relying on exceptions, e.g. file-not-found, but in reality you should still expect the exception because a file may be deleted or moved in between the statement that checks for existence and the statement that tries to open the file!
summary: in general, throwing exceptions is the safest course. Of course, directly warning or asking the user should only be done in the user-interface code!
Exceptions should be used if there is something that can be done about it, but the code that detects it can't known what to do.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
Google's Go language has no exceptions as a design choice, and Linus of Linux fame has called exceptions crap. Why?
Exceptions make it really easy to write code where an exception being thrown will break invariants and leave objects in an inconsistent state. They essentially force you to remember that most every statement you make can potentially throw, and handle that correctly. Doing so can be tricky and counter-intuitive.
Consider something like this as a simple example:
class Frobber
{
int m_NumberOfFrobs;
FrobManager m_FrobManager;
public:
void Frob()
{
m_NumberOfFrobs++;
m_FrobManager.HandleFrob(new FrobObject());
}
};
Assuming the FrobManager will delete the FrobObject, this looks OK, right? Or maybe not... Imagine then if either FrobManager::HandleFrob() or operator new throws an exception. In this example, the increment of m_NumberOfFrobs does not get rolled back. Thus, anyone using this instance of Frobber is going to have a possibly corrupted object.
This example may seem stupid (ok, I had to stretch myself a bit to construct one :-)), but, the takeaway is that if a programmer isn't constantly thinking of exceptions, and making sure that every permutation of state gets rolled back whenever there are throws, you get into trouble this way.
As an example, you can think of it like you think of mutexes. Inside a critical section, you rely on several statements to make sure that data structures are not corrupted and that other threads can't see your intermediate values. If any one of those statements just randomly doesn't run, you end up in a world of pain. Now take away locks and concurrency, and think about each method like that. Think of each method as a transaction of permutations on object state, if you will. At the start of your method call, the object should be clean state, and at the end there should also be a clean state. In between, variable foo may be inconsistent with bar, but your code will eventually rectify that. What exceptions mean is that any one of your statements can interrupt you at any time. The onus is on you in each individual method to get it right and roll back when that happens, or order your operations so throws don't effect object state. If you get it wrong (and it's easy to make this kind of mistake), then the caller ends up seeing your intermediate values.
Methods like RAII, which C++ programmers love to mention as the ultimate solution to this problem, go a long way to protect against this. But they aren't a silver bullet. It will make sure you release resources on a throw, but doesn't free you from having to think about corruption of object state and callers seeing intermediate values. So, for a lot of people, it's easier to say, by fiat of coding style, no exceptions. If you restrict the kind of code you write, it's harder to introduce these bugs. If you don't, it's fairly easy to make a mistake.
Entire books have been written about exception safe coding in C++. Lots of experts have gotten it wrong. If it's really that complex and has so many nuances, maybe that's a good sign that you need to ignore that feature. :-)
The reason for Go not having exceptions is explained in the Go language design FAQ:
Exceptions are a similar story. A
number of designs for exceptions have
been proposed but each adds
significant complexity to the language
and run-time. By their very nature,
exceptions span functions and perhaps
even goroutines; they have
wide-ranging implications. There is
also concern about the effect they
would have on the libraries. They are,
by definition, exceptional yet
experience with other languages that
support them show they have profound
effect on library and interface
specification. It would be nice to
find a design that allows them to be
truly exceptional without encouraging
common errors to turn into special
control flow that requires every
programmer to compensate.
Like generics, exceptions remain an
open issue.
In other words, they haven't yet figured out how to support exceptions in Go in a way that they think is satisfactory. They are not saying that Exceptions are bad per se;
UPDATE - May 2012
The Go designers have now climbed down off the fence. Their FAQ now says this:
We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.
Go takes a different approach. For plain error handling, Go's multi-value returns make it easy to report an error without overloading the return value. A canonical error type, coupled with Go's other features, makes error handling pleasant but quite different from that in other languages.
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.
See the Defer, Panic, and Recover article for details.
So the short answer is that they can do it differently using multi-value return. (And they do have a form of exception handling anyway.)
... and Linus of Linux fame has called exceptions crap.
If you want to know why Linus thinks exceptions are crap, the best thing is to look for his writings on the topic. The only thing I've tracked down so far is this quote that is embedded in a couple of emails on C++:
"The whole C++ exception handling thing is fundamentally broken. It's especially broken for kernels."
You'll note that he's talking about C++ exceptions in particular, and not exceptions in general. (And C++ exceptions do apparently have some issues that make them tricky to use correctly.)
My conclusion is that Linus hasn't called exceptions (in general) "crap" at all!
Exceptions are not bad per se, but if you know they are going to happen a lot, they can be expensive in terms of performance.
The rule of thumb is that exceptions should flag exceptional conditions, and that you should not use them for control of program flow.
I disagree with "only throw exceptions in an exceptional situation." While generally true, it's misleading. Exceptions are for error conditions (execution failures).
Regardless of the language you use, pick up a copy of Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition). The chapter on exception throwing is without peer. Some quotes from the first edition (the 2nd's at my work):
DO NOT return error codes.
Error codes can be easily ignored, and often are.
Exceptions are the primary means of reporting errors in frameworks.
A good rule of thumb is that if a method does not do what its name suggests, it should be considered a method-level failure, resulting in an exception.
DO NOT use exceptions for the normal flow of control, if possible.
There are pages of notes on the benefits of exceptions (API consistency, choice of location of error handling code, improved robustness, etc.) There's a section on performance that includes several patterns (Tester-Doer, Try-Parse).
Exceptions and exception handling are not bad. Like any other feature, they can be misused.
From the perspective of golang, I guess not having exception handling keeps the compiling process simple and safe.
From the perspective of Linus, I understand that kernel code is ALL about corner cases. So it makes sense to refuse exceptions.
Exceptions make sense in code were it's okay to drop the current task on the floor, and where common case code has more importance than error handling. But they require code generation from the compiler.
For example, they are fine in most high-level, user-facing code, such as web and desktop application code.
Exceptions in and of themselves are not "bad", it's the way that exceptions are sometimes handled that tends to be bad. There are several guidelines that can be applied when handling exceptions to help alleviate some of these issues. Some of these include (but are surely not limited to):
Do not use exceptions to control program flow - i.e. do not rely on "catch" statements to change the flow of logic. Not only does this tend to hide various details around the logic, it can lead to poor performance.
Do not throw exceptions from within a function when a returned "status" would make more sense - only throw exceptions in an exceptional situation. Creating exceptions is an expensive, performance-intensive operation. For example, if you call a method to open a file and that file does not exist, throw a "FileNotFound" exception. If you call a method that determines whether a customer account exists, return a boolean value, do not return a "CustomerNotFound" exception.
When determining whether or not to handle an exception, do not use a "try...catch" clause unless you can do something useful with the exception. If you are not able to handle the exception, you should just let it bubble up the call stack. Otherwise, exceptions may get "swallowed" by the handler and the details will get lost (unless you rethrow the exception).
Typical arguments are that there's no way to tell what exceptions will come out of a particular piece of code (depending on language) and that they are too much like gotos, making it difficult to mentally trace execution.
http://www.joelonsoftware.com/items/2003/10/13.html
There is definitely no consensus on this issue. I would say that from the point of view of a hard-core C programmer like Linus, exceptions are definitely a bad idea. A typical Java programmer is in a vastly different situation, though.
Exceptions aren't bad. They fit in well with C++'s RAII model, which is the most elegant thing about C++. If you have a bunch of code already that's not exception safe, then they're bad in that context. If you're writing really low level software, like the linux OS, then they're bad. If you like littering your code with a bunch of error return checks, then they not helpful. If you don't have a plan for resource control when an exception is thrown (that C++ destructors provides) then they're bad.
A great use-case for exceptions is thus....
Say you are on a project and every controller (around 20 different major ones) extends a single superclass controller with an action method. Then every controller does a bunch of stuff different from each other calling objects B, C, D in one case and F, G, D in another case. Exceptions come to the rescue here in many cases where there was tons of return code and EVERY controller was handling it differently. I whacked all that code, threw the proper exception from "D", caught it in the superclass controller action method and now all our controllers are consistent. Previously D was returning null for MULTIPLE different error cases that we want to tell the end-user about but couldn't and I didn't want to turn the StreamResponse into a nasty ErrorOrStreamResponse object (mixing a data structure with errors in my opinion is a bad smell and I see lots of code return a "Stream" or other type of entity with error info embedded in it(it should really be the function returns the success structure OR the error structure which I can do with exceptions vs. return codes)....though the C# way of multiple responses is something I might consider sometimes though in many cases, the exception can skip a whole lot of layers(layers that I don't need to clean up resources on either).
yes, we have to worry about each level and any resource cleanup/leaks but in general none of our controllers had any resources to clean up after.
thank god we had exceptions or I would have been in for a huge refactor and wasted too much time on something that should be a simple programming problem.
Theoretically they are really bad. In perfect mathematical world you cannot get exception situations. Look at the functional languages, they have no side effects, so they virtually do not have source for unexceptional situations.
But, reality is another story. We always have situations that are "unexpected". This is why we need exceptions.
I think we can think of exceptions as of syntax sugar for ExceptionSituationObserver. You just get notifications of exceptions. Nothing more.
With Go, I think they will introduce something that will deal with "unexpected" situations. I can guess that they will try to make it sound less destructive as exceptions and more as application logic. But this is just my guess.
The exception-handling paradigm of C++, which forms a partial basis for that of Java, and in turn .net, introduces some good concepts, but also has some severe limitations. One of the key design intentions of exception handling is to allow methods to ensure that they will either satisfy their post-conditions or throw an exception, and also ensure that any cleanup which needs to happen before a method can exit, will happen. Unfortunately, the exception-handling paradigms of C++, Java, and .net all fail to provide any good means of handling the situation where unexpected factors prevent the expected cleanup from being performed. This in turn means that one must either risk having everything come to a screeching halt if something unexpected happens (the C++ approach to handling an exception occurs during stack unwinding), accept the possibility that a condition which cannot be resolved due to a problem that occurred during stack-unwinding cleanup will be mistaken for one which can be resolved (and could have been, had the cleanup succeeded), or accept the possibility that an unresolvable problem whose stack-unwinding cleanup triggers an exception that would typically be resolvable, might go unnoticed as code which handles the latter problem declares it "resolved".
Even if exception handling would generally be good, it's not unreasonable to regard as unacceptable an exception-handling paradigm that fails to provide a good means for handling problems that occur when cleaning up after other problems. That isn't to say that a framework couldn't be designed with an exception-handling paradigm that could ensure sensible behavior even in multiple-failure scenarios, but none of the top languages or frameworks can as yet do so.
I havent read all of the other answers, so this ma yhave already been mentioned, but one criticism is that they cause programs to break in long chains, making it difficult to track down errors when debugging the code. For example, if Foo() calls Bar() which calls Wah() which calls ToString() then accidentily pushing the wrong data into ToString() ends up looking like an error in Foo(), an almost completely unrelated function.
For me the issue is very simple. Many programmers use exception handler inappropriately. More language resource is better. Be capable to handle exceptions is good. One example of bad use is a value that must be integer not be verified, or another input that may divide and not be checked for division of zero... exception handling may be an easy way to avoid more work and hard thinking, the programmer may want to do a dirty shortcut and apply an exception handling... The statement: "a professional code NEVER fails" might be illusory, if some of the issues processed by the algorithm are uncertain by its own nature. Perhaps in the unknown situations by nature is good come into play the exception handler. Good programming practices are a matter of debate.
Exception not being handled is generally bad.
Exception handled badly is bad (of course).
The 'goodness/badness' of exception handling depends on the context/scope and the appropriateness, and not for the sake of doing it.
Okay, boring answer here. I guess it depends on the language really. Where an exception can leave allocated resources behind, they should be avoided. In scripting languages they just desert or overjump parts of the application flow. That's dislikable in itself, yet escaping near-fatal errors with exceptions is an acceptable idea.
For error-signaling I generally prefer error signals. All depends on the API, use case and severity, or if logging suffices. Also I'm trying to redefine the behaviour and throw Phonebooks() instead. The idea being that "Exceptions" are often dead ends, but a "Phonebook" contains helpful information on error recovery or alternative execution routes. (Not found a good use case yet, but keep trying.)