Exception handling best practices inside gen_server module - exception

I just started learning Erlang and this is a module from a test project of mine. I'm doing it so that I can understand a little better how the supervision tree works, to practice fail-fast code and some programming best practices.
The udp_listener process listens for UDP messages. It's role is to listen to communication requests from other hosts in the network and contact them through TCP using the port number defined in the UDP message.
The handle_info(...) function is called every time an UDP message is received by the socket, it decodes the UDP message and passes it to the tcp_client process.
From what I understood the only failure point in my code is the decode_udp_message(Data) called sometime inside handle_info(...).
When this functions fails, is the whole udp_listener process is restarted? Should I keep this from happening?
Shouldn't just the handle_info(...) function silently die without affecting the udp_listener process?
How should I log an exception on decode_udp_message(Data)? I would like to register somewhere the host and it's failed message.
-module(udp_listener).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
%% ====================================================================
%% API functions
%% ====================================================================
-export([start_link/1]).
start_link(Port) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Port, []).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
%% init/1
%% ====================================================================
-spec init(Port :: non_neg_integer()) -> Result when
Result :: {ok, Socket :: port()}
| {stop, Reason :: term()}.
%% ====================================================================
init(Port) ->
SocketTuple = gen_udp:open(Port, [binary, {active, true}]),
case SocketTuple of
{ok, Socket} -> {ok, Socket};
{error, eaddrinuse} -> {stop, udp_port_in_use};
{error, Reason} -> {stop, Reason}
end.
% Handles "!" messages from the socket
handle_info({udp, Socket, Host, _Port, Data}, State) -> Socket = State,
handle_ping(Host, Data),
{noreply, Socket}.
terminate(_Reason, State) -> Socket = State,
gen_udp:close(Socket).
handle_cast(_Request, State) -> {noreply, State}.
handle_call(_Request, _From, State) -> {noreply, State}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
%% ====================================================================
%% Internal functions
%% ====================================================================
handle_ping(Host, Data) ->
PortNumber = decode_udp_message(Data),
contact_host(Host, PortNumber).
decode_udp_message(Data) when is_binary(Data) ->
% First 16 bits == Port number
<<PortNumber:16>> = Data,
PortNumber.
contact_host(Host, PortNumber) ->
tcp_client:connect(Host, PortNumber).
Result
I've changed my code based on your answers, decode_udp_message is gone because handle_ping does what I need.
handle_ping(Host, <<PortNumber:16>>) ->
contact_host(Host, PortNumber);
handle_ping(Host, Data) ->
%% Here I'll log the invalid datagrams but the process won't be restarted
I like the way it is now, by adding the following code I could handle protocol changes in the future without losing backwards compatibility with old servers:
handle_ping(Host, <<PortNumber:16, Foo:8, Bar:32>>) ->
contact_host(Host, PortNumber, Foo, Bar);
handle_ping(Host, <<PortNumber:16>>) ->
...
#Samuel-Rivas
tcp_client is another gen_server with it's own supervisor, it will handle its own failures.
-> Socket = State in now only present in the terminate function. gen_udp:close(Socket). is easier on the eyes.

I think that "let it crash" has often been misinterpreted as "do not handle errors" (a much stronger and stranger suggestion). And the answer to your question ("should I handle errors or not") is "it depends".
One concern with error handling is the user experience. You're never going to want to throw a stack trace an supervision tree at you users. Another concern, as Samuel Rivas points out, is that debugging from just a crashed process can be painful (especially for a beginner).
Erlang's design favors servers with non-local clients. In this architecture, the clients must be able to handle the server suddenly becoming unavailable (your wifi connection drops just as you click the "post" button on S.O.), and the servers must be able to handle sudden drop-outs from clients. In this context, I would translate "let it crash" as "since all parties can handle the server vanishing and coming back, why not use that as the error handler? Instead of writing tons of lines of code to recover from all the edge-cases (and then still missing some), just drop all the connections and return to a known-good state."
The "it depends" comes in here. Maybe it's really important to you to know who sent the bad datagram (because you're also writing the clients). Maybe the clients always want a reply (hopefully not with UDP).
Personally, I begin by writing the "success path", which includes both successful success and also the errors that I want to show clients. Everything that I didn't think of or that clients don't need to know about is then handled by the process restarting.

Your decode_message is not the only point of failure. contact_host can most likely fail too, but you are either ignoring the error tuple or handling that failure in your tcp_client implementation.
That aside, you approach to error handling would work provided that your udp_listener is started by a supervisor with the correct strategy. If Data is not exactly 16 bits then the matching will fail and the process will crash with a badmatch exception. Then the supervisor will start a new one.
Many online style guides will advertise just that style. I think they are wrong. Even though failing there right away is just what you want, it doesn't mean you cannot provide a better reason than badmatch. So I would write some better error handling there. Usually, I would throw an informative tuple, but for gen servers that is tricky because they wrap every call within a catch which will turn throws into valid values. That is unfortunate, but is a topic for other long explanation, so for practical purposes I will throw errors here. A third alternative is just to use error tuples ({ok, Blah} | {error, Reason}), however that gets complicated fast. Which option to use is also topic for a long explanation/debate, so for now I'll just continue with my own approach.
Getting back to your code, if you want proper and informative error management, I would do something in this lines with the decode_udp_message function (preserving your current semantics, see at the end of this response, since I think they are not what you wanted):
decode_udp_message(<<PortNumber:16>>) ->
PortNumber;
decode_udp_message(Ohter) ->
%% You could log here if you want or live with the crash message if that is good enough for you
erlang:error({invalid_udp_message, {length, byte_size(Other)}}).
As you have said, this will take the entire UDP connection with it. If the process is restarted by a supervisor, then it will reconnect (which will probably cause problems unless you use the reuseaddr sockopt). That will be fine unless you are planning to fail many times per second and opening the connection becomes a burden. If that is the case you have several options.
Assume that you can control all your points of failure and handle errors there without crashing. For example, in this scenario you could just ignore malformed messages. This is probably fine in simple scenarios like this, but is unsafe as it is easy to overlook points of failure.
Separate the concerns that you want to keep fault tolerant. In this case I would have one process to hold the connections and another one to decode the messages. For the latter you could use a "decoding server" or spawn one per message depending on your preferences and the load you are expecting.
Summary:
Failing as soon as your code finds something outside what is the normal behaviour is a good idea, but remember to use supervisors to restore functionality
Just-let-it-crash, is a bad practice in my experience, you should strive for clear error reasons, that will make your life easier when your systems grow
Processes are your tool to isolate the scope for failure recovery, if you don't want one system to be affected by failures/restarts, just spawn off processes to handle the complexity that you want to isolate
Sometimes performance gets in the way and you'll need to compromise and handle errors in place instead of let processes crash, but as usual, avoid premature optimisation in this sense
Some notes about your code unrelated to error handling:
Your comment in the decode_udp_message seems to imply that you want to parse the first 16 bits, but you are actually forcing Data to be exactly 16 bits.
In some of your calls you do something like -> Socket = State, that indentation is probably bad style, and also the renaming of the variable is somewhat unnecessary. You can either just change State for Socket in the function head or, if you want to make clear that your state is a socket write your function head like ..., Socket = State) ->

Related

When is it right time to throw an exception in functional programming

Say I have a web application with UserController. Client sends a HTTP POST request that is about to be handled by the controller. That however first must parse the provided json to UserDTO. For this reason there exist a UserDTOConverter with a method toDTO(json): User.
Given I value functional programming practices for its benefits of referential transparency and pure function the question is. What is the best approach to deal with a possibly inparsable json? First option would be to throw an exception and have it handled in global error handler. Invalid json means that something went terrible wrong (eg hacker) and this error is unrecoverable, hence the exception is on point (even assuming FP). The second option would be to return Maybe<User> instead of User. Then in the controller we can based on the return type return HTTP success response or failure response. Ultimately both approaches results in the same failure/success response, which one is preferable though?
Another example. Say I have a web application that needs to retrieve some data from remote repository UserRepository. From a UserController the repository is called getUser(userId): User. Again, what is the best way to handle the error of possible non existent user under provided id? Instead of returning User I can again return Maybe<User>. Then in controller this result can be handled by eg returning "204 No Content". Or I could throw an exception. The code stays referentially transparent as again I am letting the exception to bubble all the way up to global error handler (no try catch blocks).
Whereas in the first example I would lean more towards throwing an exception in the latter one I would prefer returning a Maybe. Exceptions result in cleaner code as the codebase is not cluttered with ubiquitous Eithers, Maybes, empty collections, etc. However, returning these kinds of data structure ensure explicitness of the calls, and imo results in better discoverability of the error.
Is there a place for exceptions in functional programming? What is the biggest pitfall of using exceptions over returning Maybes or Eithers? Does it make sense to be throwing exceptions in FP based app? If so is there a rule of thumb for that?
TL;DR
If there are Maybes/Eithers all over the codebase, you generally have a problem with I/O being mixed promiscuously with business logic. This doesn't get any better if you replace them with exceptions (or vice versa).
Mark Seemann has already given a good answer, but I'd like to address one specific bit:
Exceptions result in cleaner code as the codebase is not cluttered with ubiquitous Eithers, Maybes, empty collections, etc.
This isn't necessarily true. Either part.
Problem with Exceptions
The problem with exceptions is that they circumvent the normal control flow, which can make the code difficult to reason about. This seems so obvious as to barely be worthy of mention, until you end up with an error thrown 20 calls deep in a call stack where it isn't clear what triggered the error in the first place: even though the stack trace might point you to the exact line in the code you might have a very hard time figuring out the application state that caused the error to happen. The fact that you can be undisciplined about state transitions in an imperative/procedural program is of course the whole thing that FP is trying to fix.
Maybe, Maybe not: It might be Either one
You shouldn't have ubiquitous Maybes/Eithers all over the codebase, and for the same reason that you shouldn't be throwing exceptions willy-nilly all over the codebase: it complicates the code too much. You should have files that are entry points to the system, and those I/O-concerned files will be full of Maybes/Eithers, but they should then delegate to normal functions that either get lifted or dispatched to through some other mechanism depending on language (you don't specify the language). At the very least languages with option types almost always support first-class functions, you can always use a callback.
It's kind of like testability as a proxy for code quality: if your code is hard to test it probably has structural problems. If your codebase is full of Maybes/Eithers in every file it probably has structural problems.
You're asking about a couple of different scenarios, and I'll try to address each one.
Input
The first question pertains to converting a UserDTO (or, in general, any input) into a stronger representation (User). Such a conversion is usually self-contained (has no external dependencies) so can be implemented as a pure function. The best way to view such a function is as a parser.
Usually, parsers will return Either values (AKA Result), such as Either<Error, User>. The Either monad is, however, short-circuiting, meaning that if there's more than one problem with the input, only the first problem will be reported as an error.
When validating input, you often want to collect and return a list of all problems, so that the client can fix all problems and try again. A monad can't do that, but an applicative functor can. In general, I believe that validation is a solved problem.
Thus, you'll need to model validation as a type that isomomorphic to Either, but has different applicative functor behaviour, and no monad interface. The above links already show some examples, but here's a realistic C# example: An applicative reservation validation example in C#.
Data access
Data access is different, because you'd expect the data to already be valid. Reading from a data store can, however, 'go wrong' for two different reasons:
The data is not there
The data store is unreachable
The first issue (querying for missing data) can happen for various reasons, and it's usually appropriate to plan for that. Thus, a database query for a user should return Maybe<User>, indicating to the client that it should be ready to handle both cases: the user is there, or the user is not there.
The other issue is that the data store may sometimes be unreachable. This can be caused by a network partition, or if the database server is experiencing problems. In such cases, there's usually not much client code can do about it, so I usually don't bother explicitly modelling those scenarios. In other words, I'd let the implementation throw an exception, and the client code would typically not catch it (other than to log it).
In short, only throw exceptions that are unlikely to be handled. Use sum types for expected errors.
I have seen code like below, where exceptions are wrapped in a generic error. What i don't like about this approach is that we need to write a handler to deal with this UnexpectedError, inspect it, extract the exception and log it. Not sure if this is the correct way to do it.
override suspend fun update(
reservation: Reservation,
history: ReservationHistory
): Either<ReservationError, Reservation> {
return Either.catch {
mongoClient.startSession().use { clientSession ->
clientSession.startTransaction()
mongoClient.getDatabase(database)
.getCollection<ReservationDocument>()
.updateOneById(reservation.reservationId.value, MapToReservationDocument.invoke(reservation))
mongoClient.getDatabase(database)
.getCollection<ReservationHistoryDocument>()
.insertOne(MapToReservationHistoryDocument.invoke(history))
reservation
}
}.mapLeft {
UnexpectedError(it)
}
}

When would I want to resume a Perl 6 exception?

Perhaps my real question is "Is this a feature appropriate for Learning Perl 6"? Based on Should this Perl 6 CATCH block be able to change variables in the lexical scope?, it seems the simplest example might be beyond a simple example.
In that question I was working with something that appears to be silly or better down in another way for that particular problem because I was playing with the feature rather than solving a problem.
There's the documented use of warnings as special sort of exceptions ("control exceptions") where you get the message, can catch it if you like, but can also ignore it and it will resume on its own (although I was rather stupid about this in Where should I catch a Perl 6 warning control exception?).
Beyond that, I'm thinking about things where the caller can handle a failure outside of the scope of the callee. For instance, reconnecting to a database, fixing missing directories, and other external resource issues that the callee doesn't have responsibility for.
In reading about this sort of thing in other languages, the advice has mostly been to not use them because in "real world" programming people tend to not actually handle the problem anyway.
The answer to C# exception handler resume next seems to say it's poor practice and ugly code. I certainly haven't figured out a way to hide a bunch of code in the callee.
I hacked up this example, although I'm not convinced it's a good way to do it or something to recommend to beginners. The program looks for a PID file when it starts. If it finds one, it throws an exception. Handling that exception checks that the other instance is still running, which might throw a different type of exception. And, there's the one to handle the file IO problems. The trick is that the X::MyProgram::FoundSemaphore can resume if the other program isn't running (but left its PID file behind).
class X::MyProgram::FoundSemaphore is Exception {
has $.filename;
has $.this-pid = $*PID;
has $.that-pid = $!filename.lines(1);
method gist {
"Found an existing semaphore file (pid {.that-pid})"
}
}
class X::MyProgram::StillRunning is Exception {
has $.that-pid;
has $.os-error;
method gist {
"This program is already running (pid {self.that-pid})"
}
}
class X::MyProgram::IO::OpenFile is Exception {
has $.filename;
method gist {
"This program is already running (pid {self.that-pid})"
}
}
sub create-semaphore {
state $filename = "$*PROGRAM.pid";
END { unlink $filename }
die X::MyProgram::FoundSemaphore.new(
:filename($filename)
) if $filename.IO.e;
my $fh = try open $filename, :w;
# open throws Ad::Hoc, which could be more helpful
die X::MyProgram::IO::OpenFile.new(
:filename($filename),
:os-error($!), # role X::IO-ish
) unless $fh;
$fh.print: $*PID;
}
BEGIN {
try {
CATCH {
when X::MyProgram::FoundSemaphore {
my $proc = run qqw/kill -0 {.that-pid}/;
X::MyProgram::StillRunning.new(
:that-pid(.that-pid) ).throw
if $proc.so; # exit code is 0, so, True
unlink .filename;
.resume;
}
default { say "Caught {.^name}"; exit }
}
create-semaphore();
}
}
sub MAIN ( Int $delay = 10 ) {
put "$*PID sleeping for $delay seconds";
sleep $delay;
}
Resumable exceptions certainly aren't something I've found myself reaching for in Perl 6. I don't think I've used them in "userspace" code at all yet. A resumable exception turned out to be the right way to implement the emit function, used in supply and react blocks. The take function used in gather is also implemented using a resumable exception, and - as you've already discovered - warn uses them.
I suspect the last of these - warn - is the only case that the typical Perl 6 user will be interested in. Capturing warnings and sending them elsewhere - perhaps to a log file or log server - is a fairly reasonable thing to need to do. So far as Learning Perl 6 goes, that is probably the obvious useful example of a resumable exception.
I think it's significant that all of the use-cases that take advantage of resumable exceptions in Perl 6 itself turn out to be things classified as "control exceptions". Control exception are essentially normal exceptions at an implementation level: they involve a non-local transfer of control. They are made distinct at the language level because it would be rather awkward to use Perl 6 if your emit, take, warn, next, last and so forth stopped working because of a CATCH block with a default swallowing the control exceptions!
However, it's also a bit of "do as I say, not as I do": while Perl 6 is happy to use the exception system to implement non-local flow control, it somewhat fences it off in a dusty corner of the language rather than holding it up as an example of something to do. And for good reason: usually, code that uses exceptions to do flow control is hard to follow, and that goes double for resumable exceptions. The other big risk is that such exceptions can be swallowed up by code that uses a bare try or a CATCH with a default - making it a rather fragile thing to do in a larger codebase.
I'd imagine the best uses of resumable exceptions will turn out to be as an implementation strategy for things that users won't think about in terms of exceptions at all - just as is the case with take and emit (and, most of the time, warn). And, as with the existing examples of resumable exceptions, the thing being resumed will be an exception type that was specifically designed to be thrown in resumable situations and only used in cases where that is a sensible thing to do. Until Perl 6 provides a way to define custom control exceptions, however, I'd be rather reluctant to do this; the try/default swallowing issue makes it just too fragile.

Why is writing a closed TCP socket worse than reading one?

When you read a closed TCP socket you get a regular error, i.e. it either returns 0 indicating EOF or -1 and an error code in errno which can be printed with perror.
However, when you write a closed TCP socket the OS sends SIGPIPE to your app which will terminate the app if not caught.
Why is writing the closed TCP socket worse than reading it?
+1 To Greg Hewgill for leading my thought process in the correct direction to find the answer.
The real reason for SIGPIPE in both sockets and pipes is the filter idiom / pattern which applies to typical I/O in Unix systems.
Starting with pipes. Filter programs like grep typically write to STDOUT and read from STDIN, which may be redirected by the shell to a pipe. For example:
cat someVeryBigFile | grep foo | doSomeThingErrorProne
The shell when it forks and then exec's these programs probably uses the dup2 system call to redirect STDIN, STDOUT and STDERR to the appropriate pipes.
Since the filter program grep doesn't know and has no way of knowing that it's output has been redirected then the only way to tell it to stop writing to a broken pipe if doSomeThingErrorProne crashes is with a signal since return values of writes to STDOUT are rarely if ever checked.
The analog with sockets would be the inetd server taking the place of the shell.
As an example I assume you could turn grep into a network service which operates over TCP sockets. For example with inetd if you want to have a grep server on TCP port 8000 then add this to /etc/services:
grep 8000/tcp # grep server
Then add this to /etc/inetd.conf:
grep stream tcp nowait root /usr/bin/grep grep foo
Send SIGHUP to inetd and connect to port 8000 with telnet. This should cause inetd to fork, dup the socket onto STDIN, STDOUT and STDERR and then exec grep with foo as an argument. If you start typing lines into telnet grep will echo those lines which contain foo.
Now replace telnet with a program named ticker that for instance writes a stream of real time stock quotes to STDOUT and gets commands on STDIN. Someone telnets to port 8000 and types "start java" to get quotes for Sun Microsystems. Then they get up and go to lunch. telnet inexplicably crashes. If there was no SIGPIPE to send then ticker would keep sending quotes forever, never knowing that the process on the other end had crashed, and needlessly wasting system resources.
Usually if you're writing to a socket, you would expect the other end to be listening. This is sort of like a telephone call - if you're speaking, you wouldn't expect the other party to simply hang up the call.
If you're reading from a socket, then you're expecting the other end to either (a) send you something, or (b) close the socket. Situation (b) would happen if you've just sent something like a QUIT command to the other end.
Think of the socket as a big pipeline of data between the sending and the receiving process. Now imagine that the pipeline has a valve that is shut (the socket connection is closed).
If you're reading from the socket (trying to get something out of the pipe), there's no harm in trying to read something that isn't there; you just won't get any data out. In fact, you may, as you said, get an EOF, which is correct, as there's no more data to be read.
However, writing to this closed connection is another matter. Data won't go through, and you may wind up dropping some important communication on the floor. (You can't send water down a pipe with a closed valve; if you try, something will probably burst somewhere, or, at the very least, the back pressure will spray water all over the place.) That's why there's a more powerful tool to alert you to this condition, namely, the SIGPIPE signal.
You can always ignore or block the signal, but you do so at your own risk.
I think a large part of the answer is 'so that a socket behaves rather similarly to a classic Unix (anonymous) pipe'. Those also exhibit the same behaviour - witness the name of the signal.
So, then it is reasonable to ask why do pipes behave that way. Greg Hewgill's answer gives a summary of the situation.
Another way of looking at it is - what is the alternative? Should a 'read()' on a pipe with no writer give a SIGPIPE signal? The meaning of SIGPIPE would have to change from 'write on a pipe with noone to read it', of course, but that's trivial. There's no particular reason to think that it would be better; the EOF indication (zero bytes to read; zero bytes read) is a perfect description of the state of the pipe, and so the behaviour of read is good.
What about 'write()'? Well, an option would be to return the number of bytes written - zero. But that is not a good idea; it implies that the code should try again and maybe more bytes would be sent, which is not going to be the case. Another option would be an error - write() returns -1 and sets an appropriate errno. It isn't clear that there is one. EINVAL or EBADF are both inaccurate: the file descriptor is correct and open at this end (and should be closed after the failing write); there just isn't anything to read it. EPIPE means 'broken PIPE'; so, with a caveat about "this is a socket, not a pipe", it would be the appropriate error. It is probably the errno returned if you ignore SIGPIPE. It would be feasible to do this - just return an appropriate error when the pipe is broken (and never send the signal). However, it is an empirical fact that many programs do not pay as much attention to where their output is going, and if you pipe a command that will read a multi-gigabyte file into a process that quits after the first 20 KB, but it is not paying attention to the status of its writes, then it will take a long time to finish, and will be wasting machine effort while doing so, whereas by sending it a signal that it is not ignoring, it will stop quickly -- this is definitely advantageous. And you can get the error if you want it. So the signal sending has benefits to the o/s in the context of pipes; and sockets emulate pipes rather closely.
Interesting aside: while checking the message for SIGPIPE, I found the socket option:
#define SO_NOSIGPIPE 0x1022 /* APPLE: No SIGPIPE on EPIPE */

Design by contract using assertions or exceptions? [closed]

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 4 years ago.
Improve this question
When programming by contract a function or method first checks whether its preconditions are fulfilled, before starting to work on its responsibilities, right? The two most prominent ways to do these checks are by assert and by exception.
assert fails only in debug mode. To make sure it is crucial to (unit) test all separate contract preconditions to see whether they actually fail.
exception fails in debug and release mode. This has the benefit that tested debug behavior is identical to release behavior, but it incurs a runtime performance penalty.
Which one do you think is preferable?
See releated question here
The rule of thumb is that you should use assertions when you are trying to catch your own errors, and exceptions when trying to catch other people's errors. In other words, you should use exceptions to check the preconditions for the public API functions, and whenever you get any data that are external to your system. You should use asserts for the functions or data that are internal to your system.
Disabling assert in release builds is like saying "I will never have any issues whatsoever in a release build", which is often not the case. So assert shouldn't be disabled in a release build. But you don't want the release build crashing whenever errors occur either, do you?
So use exceptions and use them well. Use a good, solid exception hierarchy and ensure that you catch and you can put a hook on exception throwing in your debugger to catch it, and in release mode you can compensate for the error rather than a straight-up crash. It's the safer way to go.
The principle I follow is this: If a situation can be realistically avoided by coding then use an assertion. Otherwise use an exception.
Assertions are for ensuring that the Contract is being adhered to. The contract must be fair, so that client must be in a position to ensure it complies. For example, you can state in a contract that a URL must be valid because the rules about what is and isn't a valid URL are known and consistent.
Exceptions are for situations that are outside the control of both the client and the server. An exception means that something has gone wrong, and there's nothing that could have been done to avoid it. For example, network connectivity is outside the applications control so there is nothing that can be done to avoid a network error.
I'd like to add that the Assertion / Exception distinction isn't really the best way to think about it. What you really want to be thinking about is the contract and how it can be enforced. In my URL example above that best thing to do is have a class that encapsulates a URL and is either Null or a valid URL. It is the conversion of a string into a URL that enforces the contract, and an exception is thrown if it is invalid. A method with a URL parameter is much clearer that a method with a String parameter and an assertion that specifies a URL.
Asserts are for catching something a developer has done wrong (not just yourself - another developer on your team also). If it's reasonable that a user mistake could create this condition, then it should be an exception.
Likewise think about the consequences. An assert typically shuts down the app. If there is any realistic expectation that the condition could be recovered from, you should probably use an exception.
On the other hand, if the problem can only be due to a programmer error then use an assert, because you want to know about it as soon as possible. An exception might be caught and handled, and you would never find out about it. And yes, you should disable asserts in the release code because there you want the app to recover if there is the slightest chance it might. Even if the state of your program is profoundly broken the user just might be able to save their work.
It is not exactly true that "assert fails only in debug mode."
In Object Oriented Software Construction, 2nd Edition by Bertrand Meyer, the author leaves a door open for checking preconditions in release mode. In that case, what happens when an assertion fails is that... an assertion violation exception is raised! In this case, there is no recovery from the situation: something useful could be done though, and it is to automatically generate an error report and, in some cases, to restart the application.
The motivation behind this is that preconditions are typically cheaper to test than invariants and postconditions, and that in some cases correctness and "safety" in the release build are more important than speed. i.e. For many applications speed is not an issue, but robustness (the ability of the program to behave in a safe way when its behaviour is not correct, i.e. when a contract is broken) is.
Should you always leave precondition checks enabled? It depends. It's up to you. There is no universal answer. If you're making software for a bank, it might be better to interrupt execution with an alarming message than to transfer $1,000,000 instead of $1,000. But what if you're programming a game? Maybe you need all the speed you can get, and if someone gets 1000 points instead of 10 because of a bug that the preconditions didn't catch (because they're not enabled), tough luck.
In both cases you should ideally have catched that bug during testing, and you should do a significant part of your testing with assertions enabled. What is being discussed here is what is the best policy for those rare cases in which preconditions fail in production code in a scenario which was not detected earlier due to incomplete testing.
To summarize, you can have assertions and still get the exceptions automatically, if you leave them enabled - at least in Eiffel. I think to do the same in C++ you need to type it yourself.
See also: When should assertions stay in production code?
There was a huge thread regarding the enabling/disabling of assertions in release builds on comp.lang.c++.moderated, which if you have a few weeks you can see how varied the opinions on this are. :)
Contrary to coppro, I believe that if you are not sure that an assertion can be disabled in a release build, then it should not have been an assert. Assertions are to protect against program invariants being broken. In such a case, as far as the client of your code is concerned there will be one of two possible outcomes:
Die with some kind of OS type failure, resulting in a call to abort. (Without assert)
Die via a direct call to abort. (With assert)
There is no difference to the user, however, it's possible that the assertions add an unnecessary performance cost in the code that is present in the vast majority of runs where the code doesn't fail.
The answer to the question actually depends much more on who the clients of the API will be. If you are writing a library providing an API, then you need some form of mechanism to notify your customers that they have used the API incorrectly. Unless you supply two versions of the library (one with asserts, one without) then assert is very unlikely the appropriate choice.
Personally, however, I'm not sure that I would go with exceptions for this case either. Exceptions are better suited to where a suitable form of recovery can take place. For example, it may be that you're trying to allocate memory. When you catch a 'std::bad_alloc' exception it might be possible to free up memory and try again.
I outlined my view on the state of the matter here: How do you validate an object's internal state? . Generally, assert your claims and throw for violation by others. For disabling asserts in release builds, you can do:
Disable asserts for expensive checks (like checking whether a range is ordered)
Keep trivial checks enabled (like checking for a null pointer or a boolean value)
Of course, in release builds, failed assertions and uncaught exceptions should be handled another way than in debug builds (where it could just call std::abort). Write a log of the error somewhere (possibly into a file), tell the customer that an internal error occurred. The customer will be able to send you the log-file.
you're asking about the difference between design-time and run-time errors.
asserts are 'hey programmer, this is broken' notifications, they're there to remind you of bugs you wouldn't have noticed when they happened.
exceptions are 'hey user, somethings gone wrong' notifications (obviously you can code to catch them so the user never gets told) but these are designed to occur at run time when Joe user is using the app.
So, if you think you can get all your bugs out, use exceptions only. If you think you can't..... use exceptions. You can still use debug asserts to make the number of exceptions less of course.
Don't forget that many of the preconditions will be user-supplied data, so you will need a good way of informing the user his data was no good. To do that, you'll often need to return error data down the call stack to the bits he is interacting with. Asserts will not be useful then - doubly so if your app is n-tier.
Lastly, I'd use neither - error codes are far superior for errors you think will occur regularly. :)
I prefer the second one. While your tests may have run fine, Murphy says that something unexpected will go wrong. So, instead of getting an exception at the actual erroneous method call, you end up tracing out a NullPointerException (or equivalent) 10 stack frames deeper.
The previous answers are correct: use exceptions for public API functions. The only time you might wish to bend this rule is when the check is computationally expensive. In that case, you can put it in an assert.
If you think violation of that precondition is likely, keep it as an exception, or refactor the precondition away.
You should use both. Asserts are for your convenience as a developer. Exceptions catch things you missed or didn't expect during runtime.
I've grown fond of glib's error reporting functions instead of plain old asserts. They behave like assert statements but instead of halting the program, they just return a value and let the program continue. It works surprisingly well, and as a bonus you get to see what happens to the rest of your program when a function doesn't return "what it's supposed to". If it crashes, you know that your error checking is lax somewhere else down the road.
In my last project, I used these style of functions to implement precondition checking, and if one of them failed, I would print a stack trace to the log file but keep on running. Saved me tons of debugging time when other people would encounter a problem when running my debug build.
#ifdef DEBUG
#define RETURN_IF_FAIL(expr) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
::print_stack_trace(2); \
return; \
}; } while(0)
#define RETURN_VAL_IF_FAIL(expr, val) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
::print_stack_trace(2); \
return val; \
}; } while(0)
#else
#define RETURN_IF_FAIL(expr)
#define RETURN_VAL_IF_FAIL(expr, val)
#endif
If I needed runtime checking of arguments, I'd do this:
char *doSomething(char *ptr)
{
RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails.
// Goes away when debug off.
if( ptr != NULL )
{
...
}
return ptr;
}
I tried synthesising several of the other answers here with my own views.
Use assertions for cases where you want to disable it in production, erring toward leaving them in. The only real reason to disable in production, but not in development, is to speed up the program. In most cases, this speed up won't be significant, but sometimes code is time critical or the test is computationally expensive. If code is mission critical, then exceptions may be best despite the slow down.
If there is any real chance of recovery, use an exception as assertions aren't designed to be recovered from. For example, code is rarely designed to recover from programming errors, but it is designed to recover from factors such as network failures or locked files. Errors should not be handled as exceptions simply for being outside the control of the programmer. Rather, the predictability of these errors, compared to coding mistakes, makes them more amiable to recovery.
Re argument that it is easier to debug assertions: The stack trace from a properly named exception is as easy to read as an assertion. Good code should only catch specific types of exceptions, so exceptions should not go unnoticed due to being caught. However, I think Java sometimes forces you to catch all exceptions.
The rule of thumb, to me, is that use assert expressions to find internal errors and exceptions for external errors. You can benefit much from the following discussion by Greg from here.
Assert expressions are used to find programming errors: either errors in the program's logic itself or in errors in its corresponding implementation. An assert condition verifies that the program remains in a defined state. A "defined state" is basically one that agrees with the program's assumptions. Note that a "defined state" for a program need not be an "ideal state" or even "a usual state", or even a "useful state" but more on that important point later.
To understand how assertions fit into a program, consider a routine in
a C++ program that is about to dereference a pointer. Now should the
routine test whether the pointer is NULL before the dereferencing, or
should it assert that the pointer is not NULL and then go ahead and
dereference it regardless?
I imagine that most developers would want to do both, add the assert,
but also check the pointer for a NULL value, in order not to crash
should the asserted condition fail. On the surface, performing both the
test and the check may seem the wisest decision
Unlike its asserted conditions, a program's error handling (exceptions) refers not
to errors in the program, but to inputs the program obtains from its
environment. These are often "errors" on someone's part, such as a user
attempting to login to an account without typing in a password. And
even though the error may prevent a successful completion of program's
task, there is no program failure. The program fails to login the user
without a password due to an external error - an error on the user's
part. If the circumstances were different, and the user typed in the
correct password and the program failed to recognize it; then although
the outcome would still be the same, the failure would now belong to
the program.
The purpose of error handling (exceptions) is two fold. The first is to communicate
to the user (or some other client) that an error in program's input has
been detected and what it means. The second aim is to restore the
application after the error is detected, to a well-defined state. Note
that the program itself is not in error in this situation. Granted, the
program may be in a non-ideal state, or even a state in which can do
nothing useful, but there is no programming errorl. On the contrary,
since the error recovery state is one anticipated by the program's
design, it iss one that the program can handle.
PS: you may want to check out the similar question: Exception Vs Assertion.
See also this question:
I some cases, asserts are disabled when building for release. You may
not have control over this (otherwise, you could build with asserts
on), so it might be a good idea to do it like this.
The problem with "correcting" the input values is that the caller will
not get what they expect, and this can lead to problems or even
crashes in wholly different parts of the program, making debugging a
nightmare.
I usually throw an exception in the if-statement to take over the role
of the assert in case they are disabled
assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff

Which, and why, do you prefer Exceptions or Return codes?

My question is what do most developers prefer for error handling, Exceptions or Error Return Codes. Please be language(or language family) specific and why you prefer one over the other.
I'm asking this out of curiosity. Personally I prefer Error Return Codes since they are less explosive and don't force user code to pay the exception performance penalty if they don't want to.
update: thanks for all the answers! I must say that although I dislike the unpredictability of code flow with exceptions. The answer about return code (and their elder brother handles) do add lots of Noise to the code.
For some languages (i.e. C++) Resources leak should not be a reason
C++ is based on RAII.
If you have code that could fail, return or throw (that is, most normal code), then you should have your pointer wrapped inside a smart pointer (assuming you have a very good reason to not have your object created on stack).
Return codes are more verbose
They are verbose, and tend to develop into something like:
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
In the end, you code is a collection of idented instructions (I saw this kind of code in production code).
This code could well be translated into:
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
Which cleanly separate code and error processing, which can be a good thing.
Return codes are more brittle
If not some obscure warning from one compiler (see "phjr" 's comment), they can easily be ignored.
With the above examples, assume than someone forgets to handle its possible error (this happens...). The error is ignored when "returned", and will possibly explode later (i.e. a NULL pointer). The same problem won't happen with exception.
The error won't be ignored. Sometimes, you want it to not explode, though... So you must chose carefully.
Return Codes must sometimes be translated
Let's say we have the following functions:
doSomething, which can return an int called NOT_FOUND_ERROR
doSomethingElse, which can return a bool "false" (for failed)
doSomethingElseAgain, which can return an Error object (with both the __LINE__, __FILE__ and half the stack variables.
doTryToDoSomethingWithAllThisMess which, well... Use the above functions, and return an error code of type...
What is the type of the return of doTryToDoSomethingWithAllThisMess if one of its called functions fail ?
Return Codes are not a universal solution
Operators cannot return an error code. C++ constructors can't, too.
Return Codes means you can't chain expressions
The corollary of the above point. What if I want to write:
CMyType o = add(a, multiply(b, c)) ;
I can't, because the return value is already used (and sometimes, it can't be changed). So the return value becomes the first parameter, sent as a reference... Or not.
Exception are typed
You can send different classes for each kind of exception. Ressources exceptions (i.e. out of memory) should be light, but anything else could be as heavy as necessary (I like the Java Exception giving me the whole stack).
Each catch can then be specialized.
Don't ever use catch(...) without re-throwing
Usually, you should not hide an error. If you do not re-throw, at the very least, log the error in a file, open a messagebox, whatever...
Exception are... NUKE
The problem with exception is that overusing them will produce code full of try/catches. But the problem is elsewhere: Who try/catch his/her code using STL container? Still, those containers can send an exception.
Of course, in C++, don't ever let an exception exit a destructor.
Exception are... synchronous
Be sure to catch them before they bring out your thread on its knees, or propagate inside your Windows message loop.
The solution could be mixing them?
So I guess the solution is to throw when something should not happen. And when something can happen, then use a return code or a parameter to enable to user to react to it.
So, the only question is "what is something that should not happen?"
It depends on the contract of your function. If the function accepts a pointer, but specifies the pointer must be non-NULL, then it is ok to throw an exception when the user sends a NULL pointer (the question being, in C++, when didn't the function author use references instead of pointers, but...)
Another solution would be to show the error
Sometimes, your problem is that you don't want errors. Using exceptions or error return codes are cool, but... You want to know about it.
In my job, we use a kind of "Assert". It will, depending on the values of a configuration file, no matter the debug/release compile options:
log the error
open a messagebox with a "Hey, you have a problem"
open a messagebox with a "Hey, you have a problem, do you want to debug"
In both development and testing, this enable the user to pinpoint the problem exactly when it is detected, and not after (when some code cares about the return value, or inside a catch).
It is easy to add to legacy code. For example:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
leads a kind of code similar to:
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(I have similar macros that are active only on debug).
Note that on production, the configuration file does not exist, so the client never sees the result of this macro... But it is easy to activate it when needed.
Conclusion
When you code using return codes, you're preparing yourself for failure, and hope your fortress of tests is secure enough.
When you code using exception, you know that your code can fail, and usually put counterfire catch at chosen strategic position in your code. But usually, your code is more about "what it must do" then "what I fear will happen".
But when you code at all, you must use the best tool at your disposal, and sometimes, it is "Never hide an error, and show it as soon as possible". The macro I spoke above follow this philosophy.
I use both actually.
I use return codes if it's a known, possible error. If it's a scenario that I know can, and will happen, then there's a code that gets sent back.
Exceptions are used solely for things that I'm NOT expecting.
According to Chapter 7 titled "Exceptions" in Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, numerous rationales are given for why using exceptions over return values is necessary for OO frameworks such as C#.
Perhaps this is the most compelling reason (page 179):
"Exceptions integrate well with object-oriented languages. Object-oriented languages tend to impose constraints on member signatures that are not imposed by functions in non-OO languages. For example, in the case of constructors, operator overloads, and properties, the developer has no choice in the return value. For this reason, it is not possible to standardize on return-value-based error reporting for object-oriented frameworks. An error reporting method, such as exceptions, which is out of band of the method signature is the only option."
My preference (in C++ and Python) is to use exceptions. The language-provided facilities make it a well-defined process to both raise, catch and (if necessary) re-throw exceptions, making the model easy to see and use. Conceptually, it's cleaner than return codes, in that specific exceptions can be defined by their names, and have additional information accompanying them. With a return code, you're limited to just the error value (unless you want to define an ReturnStatus object or something).
Unless the code you're writing is time-critical, the overhead associated with unwinding the stack is not significant enough to worry about.
Exceptions should only be returned where something happens that you were not expecting.
The other point of exceptions, historically, is that return codes are inherently proprietary, sometimes a 0 could be returned from a C function to indicate success, sometimes -1, or either of them for a fail with 1 for a success. Even when they are enumerated, enumerations can be ambiguous.
Exceptions can also provide a lot more information, and specifically spell out well 'Something Went Wrong, here's what, a stack trace and some supporting information for the context'
That being said, a well enumerated return code can be useful for a known set of outcomes, a simple 'heres n outcomes of the function, and it just ran this way'
Always use exceptions by default, BUT consider providing an additional tester-doer option (TryX)!
For me the answer is really clear. When the context dictates a Try or Tester-Doer pattern (ie cpu intensive or public api), I will ADDITIONALLY provide those methods to the exception throwing version. I think blanket rules of avoiding exceptions are misguided, unsupported, and likely cause far more expense in terms of bugs, than any performance issues they claim to prevent.
No, Microsoft does NOT say to not use exceptions (common misinterpretation).
It says if you're designing an API provide ways to help a user of that API to avoid THROWING exceptions if they need too (Try and Tester-Doer patterns)
❌ DO NOT use exceptions for the normal flow of control, if possible.
Except for system failures and operations with potential race
conditions, framework designers should design APIs so users can write
code that does not throw exceptions. For example, you can provide a
way to check preconditions before calling a member so users can write
code that does not throw exceptions.
What is inferred here is that the non-tester-doer/non-try implementation SHOULD throw an exception upon failure and then the user CAN change that to one of your tester-doer or try methods for performance. Pit of success is maintained for safety and the user OPTS INTO the more dangerous but more performant method.
Microsoft DOES say to NOT use return codes TWICE, here:
❌ DO NOT return error codes.
Exceptions are the primary means of reporting errors in frameworks.
✔️ DO report execution failures by throwing exceptions.
and here:
❌ DO NOT use error codes because of concerns that exceptions might
affect performance negatively.
To improve performance, it is possible to use either the Tester-Doer
Pattern or the Try-Parse Pattern, described in the next two sections.
If you're not using exceptions you're probably breaking this other rule of returning return codes or booleans from a non-tester/non-try implementation.
Again, TryParse does not replace Parse. It is provided in addition to Parse
MAIN REASON: Return codes fail the "Pit of Success" test for me almost every time.
It is far too easy to forget to check a return code and then have a red-herring error later on.
var success = Save()? How much performance is worth someone forgetting an if check here?
var success = TrySave()? Better, but are we going to abuse everything with the TryX pattern? Did you still provide a Save method?
Return codes don't have any of the great debugging information on them like call stack, inner exceptions.
Return codes do not propagate which, along with the point above, tends to drive excessive and interwoven diagnostic logging instead of logging in one centralized place (application and thread level exception handlers).
Return codes tend to drive messy code in the form of nested 'if' blocks
Developer time spent debugging an unknown issue that would otherwise have been an obvious exception (pit of success) IS expensive.
If the team behind C# didn't intend for exceptions to govern control flow, execeptions wouldn't be typed, there would be no "when" filter on catch statements, and there would be no need for the parameter-less 'throw' statement.
Regarding Performance:
Exceptions may be computationally expensive RELATIVE to not throwing at all, but they're called EXCEPTIONS for a reason. Speed comparisons always manage to assume a 100% exception rate which should never be the case. Even if an exception is 100x slower, how much does that really matter if it only happens 1% of the time?
Context is everything. For example, A Tester-Doer or Try option to avoid a unique key violation is likely to waste more time and resources on average (checking for existance when a collision is rare) than just assuming a successful entry and catching that rare violation.
Unless we're talking floating point arithmetic for graphics applications or something similar, CPU cycles are cheap compared to developer time.
Cost from a time perspective carries the same argument. Relative to database queries or web service calls or file loads, normal application time will dwarf exception time. Exceptions were nearly sub-MICROsecond in 2006
I dare anybody that works in .net, to set your debugger to break on all exceptions and disable just my code and see how many exceptions are already happening that you don't even know about.
Jon Skeet says "[Exceptions are] not slow enough to make it worth avoiding them in normal use". The linked response also contains two articles from Jon on the subject. His generalized theme is that exceptions are fine and if you're experiencing them as a performance problem, there's likely a larger design issue.
In Java, I use (in the following order):
Design-by-contract (ensuring preconditions are met before trying anything that might fail). This catches most things and I return an error code for this.
Returning error codes whilst processing work (and performing rollback if needed).
Exceptions, but these are used only for unexpected things.
I dislike return codes because they cause the following pattern to mushroom throughout your code
CRetType obReturn = CODE_SUCCESS;
obReturn = CallMyFunctionWhichReturnsCodes();
if (obReturn == CODE_BLOW_UP)
{
// bail out
goto FunctionExit;
}
Soon a method call consisting of 4 function calls bloats up with 12 lines of error handling.. Some of which will never happen. If and switch cases abound.
Exceptions are cleaner if you use them well... to signal exceptional events .. after which the execution path cannot continue. They are often more descriptive and informational than error codes.
If you have multiple states after a method call that should be handled differently (and are not exceptional cases), use error codes or out params. Although Personaly I've found this to be rare..
I've hunted a bit about the 'performance penalty' counterargument.. more in the C++ / COM world but in the newer languages, I think the difference isn't that much. In any case, when something blows up, performance concerns are relegated to the backburner :)
I wrote a blog post about this a while ago.
The performance overhead of throwing an exception should not play any role in your decision. If you're doing it right, after all, an exception is exceptional.
A great piece of advice I got from The Pragmatic Programmer was something along the lines of "your program should be able to perform all its main functionality without using exceptions at all".
I have a simple set of rules:
1) Use return codes for things you expect your immediate caller to react to.
2) Use exceptions for errors that are broader in scope, and may reasonable be expected to be handled by something many levels above the caller so that awareness of the error does not have to percolate up through many layers, making code more complex.
In Java I only ever used unchecked exceptions, checked exceptions end up just being another form of return code and in my experience the duality of what might be "returned" by a method call was generally more of a hinderance than a help.
I use Exceptions in python in both Exceptional, and non-Exceptional circumstances.
It is often nice to be able to use an Exception to indicate the "request could not be performed", as opposed to returning an Error value. It means that you /always/ know that the return value is the right type, instead of arbitarily None or NotFoundSingleton or something. Here is a good example of where I prefer to use an exception handler instead of a conditional on the return value.
try:
dataobj = datastore.fetch(obj_id)
except LookupError:
# could not find object, create it.
dataobj = datastore.create(....)
The side effect is that when a datastore.fetch(obj_id) is run, you never have to check if its return value is None, you get that error immediately for free. This is counter to the argument, "your program should be able to perform all its main functionality without using exceptions at all".
Here is another example of where exceptions are 'exceptionally' useful, in order to write code for dealing with the filesystem that isn't subject to race conditions.
# wrong way:
if os.path.exists(directory_to_remove):
# race condition is here.
os.path.rmdir(directory_to_remove)
# right way:
try:
os.path.rmdir(directory_to_remove)
except OSError:
# directory didn't exist, good.
pass
One system call instead of two, no race condition. This is a poor example because obviously this will fail with an OSError in more circumstances than the directory doesn't exist, but it's a 'good enough' solution for many tightly controlled situations.
I believe the return codes adds to code noise. For example, I always hated the look of COM/ATL code due to return codes. There had to be an HRESULT check for every line of code. I consider the error return code is one of the bad decisions made by architects of COM. It makes it difficult to do logical grouping of the code, thus code review becomes difficult.
I am not sure about the performance comparison when there is an explicit check for the return code every line.
Exceptions are not for error handling, IMO. Exceptions are just that; exceptional events that you did not expect. Use with caution I say.
Error codes can be OK, but returning 404 or 200 from a method is bad, IMO. Use enums (.Net) instead, that makes the code more readable and easier to use for other developers. Also you don't have to maintain a table over numbers and descriptions.
Also; the try-catch-finally pattern is an anti-pattern in my book. Try-finally can be good, try-catch can also be good but try-catch-finally is never good. try-finally can often times be replaced by a "using" statement (IDispose pattern), which is better IMO. And Try-catch where you actually catch an exception you're able to handle is good, or if you do this:
try{
db.UpdateAll(somevalue);
}
catch (Exception ex) {
logger.Exception(ex, "UpdateAll method failed");
throw;
}
So as long as you let the exception continue to bubble it's OK. Another example is this:
try{
dbHasBeenUpdated = db.UpdateAll(somevalue); // true/false
}
catch (ConnectionException ex) {
logger.Exception(ex, "Connection failed");
dbHasBeenUpdated = false;
}
Here I actually handle the exception; what I do outside of the try-catch when the update method fails is another story, but I think my point has been made. :)
Why is then try-catch-finally an anti-pattern? Here's why:
try{
db.UpdateAll(somevalue);
}
catch (Exception ex) {
logger.Exception(ex, "UpdateAll method failed");
throw;
}
finally {
db.Close();
}
What happens if the db object has already been closed? A new exception is thrown and it has to be handled! This is better:
try{
using(IDatabase db = DatabaseFactory.CreateDatabase()) {
db.UpdateAll(somevalue);
}
}
catch (Exception ex) {
logger.Exception(ex, "UpdateAll method failed");
throw;
}
Or, if the db object does not implement IDisposable do this:
try{
try {
IDatabase db = DatabaseFactory.CreateDatabase();
db.UpdateAll(somevalue);
}
finally{
db.Close();
}
}
catch (DatabaseAlreadyClosedException dbClosedEx) {
logger.Exception(dbClosedEx, "Database connection was closed already.");
}
catch (Exception ex) {
logger.Exception(ex, "UpdateAll method failed");
throw;
}
That's my 2 cents anyway! :)
I generally prefer return codes because they let the caller decide whether the failure is exceptional.
This approach is typical in the Elixir language.
# I care whether this succeeds. If it doesn't return :ok, raise an exception.
:ok = File.write(path, content)
# I don't care whether this succeeds. Don't check the return value.
File.write(path, content)
# This had better not succeed - the path should be read-only to me.
# If I get anything other than this error, raise an exception.
{:error, :erofs} = File.write(path, content)
# I want this to succeed but I can handle its failure
case File.write(path, content) do
:ok => handle_success()
error => handle_error(error)
end
People mentioned that return codes can cause you to have a lot of nested if statements, but that can be handled with better syntax. In Elixir, the with statement lets us easily separate a series of happy-path return value from any failures.
with {:ok, content} <- get_content(),
:ok <- File.write(path, content) do
IO.puts "everything worked, happy path code goes here"
else
# Here we can use a single catch-all failure clause
# or match every kind of failure individually
# or match subsets of them however we like
_some_error => IO.puts "one of those steps failed"
_other_error => IO.puts "one of those steps failed"
end
Elixir still has functions that raise exceptions. Going back to my first example, I could do either of these to raise an exception if the file can't be written.
# Raises a generic MatchError because the return value isn't :ok
:ok = File.write(path, content)
# Raises a File.Error with a descriptive error message - eg, saying
# that the file is read-only
File.write!(path, content)
If I, as the caller, know that I want to raise an error if the write fails, I can choose to call File.write! instead of File.write.
Or I can choose to call File.write and handle each of the possible reasons for failure differently.
Of course it's always possible to rescue an exception if we want to. But compared to handling an informative return value, it seems awkward to me. If I know that a function call can fail or even should fail, its failure isn't an exceptional case.
With any decent compiler or runtime environment exceptions do not incur a significant penalty. It's more or less like a GOTO statement that jumps to the exception handler. Also, having exceptions caught by a runtime environment (like the JVM) helps isolating and fixing a bug a lot easier. I'll take a NullPointerException in Java over a segfault in C any day.
I prefer to use exceptions for error handling and return values (or parameters) as the normal result of a function. This gives an easy and consistent error-handling scheme and if done correctly it makes for much cleaner looking code.
One of the big differences is that exceptions force you to handle an error, whereas error return codes can go unchecked.
Error return codes, if used heavily, can also cause very ugly code with lots of if tests similar to this form:
if(function(call) != ERROR_CODE) {
do_right_thing();
}
else {
handle_error();
}
Personally I prefer to use exceptions for errors that SHOULD or MUST be acted upon by the calling code, and only use error codes for "expected failings" where returning something is actually valid and possible.
There is many reason to prefer Exceptions over return code:
Usually, for readibility, people try to minimize the number of return statement in a method. Doing so, exceptions prevent to do some extra work while in a incoorect state, and thus prevent to potentially damage more data.
Exception are generally more verbose arn more easilly extensible than return value. Assume that a method return natural number and that you use negative numbers as return code when an error occurs, if the scope of you method change and now return integers, you'll have to modify all the method calls instead of just tweaking a little bit the exception.
Exceptions allows more easilly to separate error handling of normal behaviour. They allows to ensure that some operations performs somehow as an atomic operation.
I only use exceptions, no return codes. I'm talking about Java here.
The general rule I follow is if I have a method called doFoo() then it follows that if it doesn't "do foo", as it were, then something exceptional has happened and an Exception should be thrown.
One thing I fear about exceptions is that throwing an exception will screw up code flow. For example if you do
void foo()
{
MyPointer* p = NULL;
try{
p = new PointedStuff();
//I'm a module user and I'm doing stuff that might throw or not
}
catch(...)
{
//should I delete the pointer?
}
}
Or even worse what if I deleted something I shouldn't have, but got thrown to catch before I did the rest of the cleanup. Throwing put a lot of weight on the poor user IMHO.
My general rule in the exception vs. return code argument:
Use errorcodes when you need localization/internationalization -- in .NET, you could use these errorcodes to reference a resource file which will then display the error in the appropriate language. Otherwise, use exceptions
Use exceptions only for errors that are really exceptional. If it's something that happens fairly often, either use a boolean or an enum errorcode.
I don't find return codes to be less ugly than exceptions. With the exception, you have the try{} catch() {} finally {} where as with return codes you have if(){}. I used to fear exceptions for the reasons given in the post; you don't know if the pointer needs to be cleared, what have you. But I think you have the same problems when it comes to the return codes. You don't know the state of the parameters unless you know some details about the function/method in question.
Regardless, you have to handle the error if possible. You can just as easily let an exception propagate to the top level as ignore a return code and let the program segfault.
I do like the idea of returning a value (enumeration?) for results and an exception for an exceptional case.
For a language like Java, I would go with Exception because the compiler gives compile time error if exceptions are not handled.This forces the calling function to handle/throw the exceptions.
For Python, I am more conflicted. There is no compiler so it's possible that caller does not handle the exception thrown by the function leading to runtime exceptions. If you use return codes you might have unexpected behavior if not handled properly and if you use exceptions you might get runtime exceptions.
There are some important aspects that remain unmentioned in this - very interesting - discussion so far.
First, it is important to note that exceptions don't apply to distributed computing, but error codes still do. Imagine communicating services distributed over multiple servers. Some communication might even be asynchronous. And the services might even use different technology stacks. Cleary, an error-handling concept is crucial here. And clearly, exceptions can't be used in this most general case, since errors have to be serialized things sent "through the cable", perhaps even in a language-neutral way. From that angle, error codes (really, error messages) are more universal than exceptions. One needs good error-message Kung Fu once one assumes a system-architect view and things need to scale.
The second point is a very different, it is about if or how a language represents discriminated unions. The question was strictly speaking about "error codes". And so were some the answers, mentioning that error codes cannot transport information as nicely as exceptions. This is true if an error code is a number. But for a fairer contrasting with exceptions, one should probably consider error values of discriminated union type. So, the return value of the callee would be of discriminated union type, and it would either be the desired happy-path value or the payload the exception would otherwise have. How often this approach is elegant enough to be preferable depends on the programming language. For example, F# has super elegant discriminated unions and the according pattern matching. In such a language it would be more seductive to avoid exceptions than in, say, C++.
The third and final point is about functional programming and pure functions. Exceptions are (in a practical way and in a theoretical-computer-science way) "side effects". In other words, functions or methods that deal with exceptions are not pure. (One practical consequence is that with exceptions one must pay attention to evaluation order.) By contrast, error values are pure, because the are just ordinary return values, with no side effects involved. Therefore, functional programmers may more likely frown upon exceptions than object-oriented programmers. (In particular if the language also has an elegant representation of the aforementioned discriminated unions.)
I prefer exceptions over return codes.
Consider a scenario when I call a function foo and forget to handle potential errors (exceptions).
If errors in foo are passed via return codes (error codes),
at compile time, I won't be alerted
if I run the code
if the error doesn't happen, I don't notice my mistake
if the error happens but doesn't affect the code after calling foo, I don't notice my mistake
if the error happens and affects the code after calling foo, I notice my mistake, but it may be hard to locate the problem
If errors in foo are passed via exceptions that are thrown,
at compile time, I won't be alerted
if I run the code
if the error doesn't happen, I don't notice my mistake
if the error happens, I'm assured to notice my mistake
From the comparison above, my conclusion is that exceptions are better than error codes.
However, exceptions are not perfect. There are at least two critical problems:
As discussed above, if I forget to handle a potential exception, I won't be alerted until I run the code and the exception is actually thrown.
It's hard to determine all the exceptions foo may throw, especially when foo calls other functions (which may also throw exceptions).
A feature in Java, “checked exceptions”, solves both problems.
In Java, when defining a function foo, I'm required to explicitly specify what exceptions it may throw using the throws keyword. Example:
private static void foo() throws FileNotFoundException {
File file = new File("not_existing_file.txt");
FileInputStream stream = new FileInputStream(file);
}
If I call foo and forget to handle the potential FileNotFoundException, the compiler produces an error. I get alerted at compile time (problem 1 solved). And all possible exceptions are listed explicitly (problem 2 solved).