In my mind, the biggest argument is the difference in what happens when the programmer makes an error. Forgetting to handle an error is a very common and easy mistake to make.
If you return error codes, it is possible to silently ignore an error. For example, if malloc fails, it returns NULL
and sets the global errno
. So correct code should do
void* myptr = malloc(1024);
if (myptr == NULL) {
perror("malloc");
exit(1);
}
doSomethingWith(myptr);
But it is very easy and expedient to instead only write:
void* myptr = malloc(1024);
doSomethingWith(myptr);
which will unexpectedly pass NULL
into your other procedure and likely discard the errno
which was carefully set. There is nothing visibly wrong with the code to indicate that this is possible.
In a language that uses exceptions, instead you would write
MyCoolObject obj = new MyCoolObject();
doSomethingWith(obj);
In this (Java) example, the new
operator either returns a valid initialized object or throws OutOfMemoryError
. If a programmer must handle this, they can catch it. In the usual (and conveniently, also the lazy) case where it is a fatal error, the exception propagation terminates the program in a relatively clean and explicit manner.
That is one reason why exceptions, when properly used, can make writing clear and safe code much easier. This pattern applies to many, many things which can go wrong, not just allocating memory.
return
, you see how “swallowing” one is a pretty nonsensical thing to do.