Hacker News new | ask | show | jobs
by lelanthran 842 days ago
> While I agree with last two paragraphs, C is not good even for that purpose because it doesn't give any tool to manage them.

The "purpose" here is to instill a sense of paranoia around error handling, so that the resulting program from the PoV of the user appears to handle whatever bizarre combination of inputs the user put in.

It's the difference between telling the user "Failed to open foo.txt (file not found). Do you want to create it?" and ending the program with a 100-line stack trace with "FileNotFoundException" buried somewhere in there.

As the article says, checked exceptions are not the solution here.

I've literally never seen, in a professional working environment, exception languages (Java, C#, Python, etc) actually check that the file they tried opening was actually opened, and if it wasn't, directing the user with a sensible message that allowed the user/operator to fix the problem.

In C, the very first time that you fail to check that `fopen` returned non-NULL, the program crashes. Then you check if it returned NULL, and need to put something in the handling code, so you look at what `errno` has, or convert `errno` to a string.

I will bet good money that you could grab the nearest C#/Java/Python/JS/etc engineer to you, ask them to find the most recent code they wrote that opened and read/wrote a file, and you'll find that there is literally no code to direct the user if the file-open failed. The default runtime handler steps in and vomits a stack trace onto the screen.

In C, you are forced to perform the NULL-check, or crash. Sure, many devs are simply going to have a no-op code-path for the error cases, doing `if ((inf = fopen(...)) != NULL) { DoSomethingWith(inf);}`, but proceeding on success is a code-smell and easy to visually spot as an error.

The exception languages make it virtually impossible to spot the code-smell of handled (or improperly handled) exceptions, and make it easy because the dev can just read the stack trace, create the file needed, and proceed with programming the rest of the app.

What a good program must do when a file open failure is encountered is direct the user in some way that they can fix the problem. For example "file doesn't exist. Create it [button], Choose a file [button]", or "Permission denied. Try running as a different user.", or "File is locked. Are you already running $PROGRAM?", or "$FOO is a directory. Specify a file.".

[EDIT: Yes, seeing a stack trace in a shipped product is one of my personal bugbears that I feel very strongly about. If it's a stack trace for an expected error (like failure to open/read/write a file) I absolutelydo get annoyed by this public display of laziness. And yes, this is one of those hills I'll die on before I leave it!]

Even the simplest c/line programs annoy me no end when the application simply dumps a stack trace to the screen. Sure, I can dig into it, but the average user is going to ask for help on stackoverflow, just to figure out what must be done to fix the error.

2 comments

> As the article says, checked exceptions are not the solution here.

Java had a very bad model of checked exceptions (the OP was written in 2008). A correct way is to make it a part of the type system, though it doesn't have to be a sum type like Rust, and make any error-related code path as convenient as possible to use.

> In C, you are forced to perform the NULL-check, or crash.

You don't necessarily crash if you failed to perform a NULL check! That's literally the single biggest problem with C's undefined behaviors. C looks like forcing checks only because most programmers do understand crashes are bad, so they do prepare for trivial or demonstrated crashes. But that's not guaranteed, and they can't easily prepare for non-crash failures without additional tools.

> You don't necessarily crash if you failed to perform a NULL check!

In the case of using the result from `fopen`, I don't know of a platform where a dereferencing of NULL (which happens in a separate translation unit, which is already compiled and linked, and will not be subject to LTO and other optimisations) within the various read/write/seek/tell functions doesn't result in an immediate crash.

I fully admit that this is applicable only to this particular example, and to all the functions in the stdlib. Everywhere else (code you wrote, that will be subject to aggressive optimisation, for example), you may not necessarily crash on a NULL dereference.

In the sense of instilling a sense of paranoia, the relative frequency of crashing due to UB is high enough that it does develop the sense of paranoia.

That's a really huge asterisk that wasn't present in your original claim ;-)

> In the sense of instilling a sense of paranoia, the relative frequency of crashing due to UB is high enough that it does develop the sense of paranoia.

Paranoia isn't a cure however. A good programmer will and arguably should develop an instinct to avoid C for most cases instead. I too have written tons of C codes, and yet I feel really uneasy about using C at all. I can't believe that C merely induces the sense of paranoia.

> I don't know of a platform where a dereferencing of NULL (which happens in a separate translation unit, which is already compiled and linked, and will not be subject to LTO and other optimisations) within the various read/write/seek/tell functions doesn't result in an immediate crash.

Although in a very different content, I have seen "dereferencing" a null pointer in C++ not crash immediately, if you dereference it to call a nonvirtual class member function, e.g,

    t->foo();
Depending on how this gets compiled and the implementation of `foo()`, the segfault may not come at the line above, where technically `t` is being dereferenced. It may come inside `foo`, or somewhere further down the call chain. The resulting crash may not even manifest as a segfault.
> Although in a very different content, I have seen "dereferencing" a null pointer in C++ not crash immediately,

I don't think this is possible at all in C, which doesn't have classes, and the sophisticated following of pointers to find a method.

Eh Java/js or whatever developers working on user applications can be very user minded simply by the fact that they work on a lot of user facing applications. I’ll redirect the user if it makes sense to me as a product. I don’t buy it that C forcing you to do a handle the input is advantageous in this regard.