Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Allowing unchecked exceptions in languages without explicit error handling or the return of error values is a mistake IMO! Makes it impossible to call a function safely


Exactly, C++ exceptions are horrible, you never know what throws what. Java made C++ exceptions better by making them explicit, so you always know what throws. Then Kotlin came and made everything a unusable mess (don't get me wrong, I love Kotlin, just hate that it doesn't have explicit exceptions).

I honestly think the best error handling strategy is employed by Zig, then Rust. they're very explicit while not getting in the way, you always know what throws what and what doesn't.

Rust has a little issue though, which is that people can make their functions return `Result<T, Box<dyn Error>>` which makes handling the returned error difficult, Zig does not have any of that.


As far as I know Zig errors can't carry any data along. It's nice having more details available about the failure.

Take for example if you call to an OS API and it returns an error that wasn't documented to be returned from that function. You can't return that code up the callstack using Zig's error mechanism. Instead there's functions such as unexpectedErrno and unexpectedError. Those call the appropriate method to get a string representation of the error and call std.debug.print to display it, then they just return error.Unexpected.

That means that the caller doesn't have any control over how the error is displayed. Meanwhile in Rust, you can add an UnexpectedError variant to your enum and let it carry an error code with it. The caller can then display that error however they want.

I don't hate Zig's error handling by any means, but personally I think Rust does it better. I'm happy to see that C++ seems to be going with the Rust way by adding a Result-like type std::expected.


Both have their issues, but I think the perfect error handling mechanism would be something like the Zig way with additional data.


It is true that C++ exceptions are not checked and are rarely part of the interface signature but C++ exceptions are not a tool for general error handling in the context you're comparing them against Java, Zig and Rust. Java uses exceptions for control flow while C++ doesn't. Zig and Rust don't have exceptions at all. What Zig and Rust have as error handling mechanisms C++ has them too. C++ exceptions are of a literal meaning - exceptional cases which you don't expect to happen and you usually don't know how to recover from. In this case propagating the exception up to the thread entry point and doing whatever in that case (logging, terminating, restarting, etc.) is vastly better than language forcing me to check for each possible exception all the way through the whole function callstack - it's useless.


> It is true that C++ exceptions are not checked and are rarely part of the interface signature but C++ exceptions are not a tool for general error handling

They’re literally the only way to report ctor errors.

> What Zig and Rust have as error handling mechanisms C++ has them too.

Only in the most reductive way that all langages are turing complete, in which case Java has them as well.


> They’re literally the only way to report ctor errors.

The point being? Please go find me a codebase which wraps object instantiation in try-catch clause if that's what you are trying to say. I haven't seen any.

Anyway, for that reason constructors are always written so that they cannot fail in hideous ways, and if there is no other way around it there is always 2-phase initialization (personally I think it's an anti-pattern).

> Only in the most reductive way that all langages are turing complete, in which case Java has them as well.

Nonsense. Zig AFAIU abstracts error types as some sort of integer enums while OTOH Rust has Result<T, E>. C++ has both of those mechanisms baked either into the language (enum) or standard library (std::expected, std::optional).


In what way are Java exceptions part of flow control and C++ exceptions aren't?


Although theoretically possible there will be rarely any C++ codebase that will use exceptions for control flow.


Honestly who cares if they are? Error handling is control flow.


I think the way Go does error handling is the best compromise.


Go’s solution is like people couldn’t decide on if they should turn left, or turn right to avoid the cliff, so to make everyone happy they drove straight into it.

It is literally the compiler enforced shitty error handling from C’s errno that is not even a sum type.


It's one of the worst I've seen. It adds so much cognitive overhead to reading the code and makes it more annoying to write. Every function call that can fail is followed by 3 lines to check for and return the error. The actual logic quickly gets lost in there.

If it wasn't for the error handling, I would probably use Go for some of my projects. It compiles to a native, self-contained binary and still has the convenience of a garbage collector, and of course has plenty of libs available due to popularity.


It does not have all that undpredictability of exceptions, and does not have nasty error handling logic of C. Do you know better alternative?


It’s honestly the worst compromise. You can trivially ignore any error in Go and they don’t contain contextual information about the call stack.


ABility to ignore errors is a plus in fact. You can skip trivial non-critical problems and carry on without crashing the whole app, like it is normally done in C and C++.


It would be if you would have to explicitly opt into it via an ignored variable like _ or something. But currently with go you can accidentally ignore the error when you don't mean to.


It's a successful compromise, but probably not the best. It has a huge amount of little issues that simply aren't there with FP error handling.


In Java, no method call is safe (Haskell/Rust safe), the langage will hapilly throws a null pointer exception or an out of memory error.

But I don't think C#, Kotlin or Scala are less safe than Java even if they do not have the concept of checked exceptions.


Haskell and Rust can also throw exception/panic respectively in any call, ever.


Technically true, but Haskell forces you to use the IO monad and you can not really recover from a panic in Rust, the thread is dead.


  head []
No monad, nothing, just an exception.


Good point...


The entire community disagrees with you[1]

>Makes it impossible to call a function safely ?? catch(Exception e) anyone?

[1] https://literatejava.com/exceptions/checked-exceptions-javas...


A lot of people, probably. But definitely not the entire community [0].

[0] https://www.yegor256.com/2015/07/28/checked-vs-unchecked-exc...


I would argue the opposite: Junior and senior developers alike catch and swallow or log checked exceptions all over the place, making it impossible to know if your method call was successful or not.


This is why you should have code reviews. Letting juniors submit code without reviewing it is a recipe for disaster, no matter which language you are programming in. And if it's a senior, well someone should have a word with him ..


At some point you want to swallow all checked exceptions at some point, because letting the application leak stack traces to the client is generally a bad idea.


What does it mean to call a function safely?

I have a desktop application with a single exception handler wrapping it's event loop. The handler display the message to the user and returns the to loop. It's rock solid -- nothing can crash it. The user can try to save their file to some network share that just disappeared and when that fails just save again somewhere else.

But there is no such thing a safe function to call. Whether you handle errors by meticulously passing them around everywhere or not makes little difference.


Java just needs one small change to fix exceptions.

Instead of the exception type being checked or unchecked, the throw should specify checked or unchecked.

So for example the first time the exception happens it can throw a checked exception for the caller to deal with.

If the caller doesn't deal with it it can simply throw its way all the way to the top without every single function needing to declare they handle it.


The proper solution is effectful type systems that are polymorphic to effects, like exceptions.


(To clarify, i'm against exception based error handling, but removing checked exceptions in languages without other mechanisms of explicit error handling makes things more brittle. For example in kotlin, where some Java practices to throw all over the place are still in use, unchecked exceptions makes things worse. You wont know if a function will throw unless you inspect the code or trust it's documentation)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: