I also hate error "handling" in Python, but I thankfully long ago realized that what I want is error propagation and reporting", and almost all of my use cases for "handling" errors were mistakes: what I wanted was a mechanism similar to panic that could propagate rich error context (as opposed to just strings), and that is what unchecked exceptions are. Most of my Python code that tried to "handle" errors now gets removed and replaced with better semantics: the kind of failure semantics that one might expect to get from panic, but can't, as panic loses too much context to be useful for reporting (forcing developers to use case logic).
Rust programs are littered with calls to unwrap and expansions of try! for the sole purpose of doing manual error propagation, which is both verbose in the code and slow at runtime (as the successful case must constantly produce and then check error results), just so the error can get to the top level somewhere and be reported to someone (which in turn drags a surprisingly large amount of your code into the domain of "functions that can return errors"). None of that boilerplate should be required: even with an insistence on the mechanism, it is such a clear case of boilerplate that a friend of mine finally ended up working on a compiler plug-in whose purpose was to jusy add calls to try! around anything that seemed to require those calls.
Clearly ;P. I just hope you realize that you sound a lot like the people working in languages like C or Java or Go that insist that adding abstraction and automation is bad as they want everything to be explicit and transparent :(.
That's absurd. I wrote the OP, and the entire article is about using abstraction to make error handling more convenient. Here's an excerpt from my post that describes my true feelings on the matter (because we don't all fit nice and neatly into pre-conceived buckets):
> My advice boils down to this: use good judgment. There’s a reason why the words “never do X” or “Y is considered harmful” don’t appear in my writing. There are trade offs to all things, and it is up to you as the programmer to determine what is acceptable for your use cases. My goal is only to help you evaluate trade offs as accurately as possible.
Maybe this is going to be "way too Hacker News", but to me this is "bulb paradox": C developers could claim it is absurd to say their arguments for explicit and transparent logic are somehow against abstraction and automation because they, unlike their assembly brethren, opted for structured control flow and rigid function conventions, and Java developers can say the same about the way they abstracted explicit polymorphism. That doesn't change the form of the argument they are making about going even further.
Having to litter code with unwrap and try! just because somewhere much lower an error is used and much higher an error is reported does not seem beneficial unless the alternative is to do it in a way that is even more manual and less safe, but that's not how it works in the languages I use on a daily basis. Yes: C and Go make dealing with errors so painful and dangerous that Rust is absolutely amazing in comparison... but I don't program in C or Go and I would hope we aren't using them as the benchmark when we have C++.
I encourage you to read my example with type declaration that I wrote elsewhere in this thread.
Recognizing trade offs is not the same as the blub paradox. Note that I responded that way because you literally accused me of not liking abstraction at all: "you sound a lot like the people working in languages like C or Java or Go that insist that adding abstraction and automation is bad as they want everything to be explicit and transparent." If you have a more nuanced argument to make, then do so, but I'm not going to let you paint me into a box.
See pcwalton's comments in this thread on exceptions.
Read that quote again: I say "add" because I mean it; I would never argue that a C developer does not like abstraction at all because that is obviously false (functions, control flow, text macros). You can't say that I "literally" said you don't like abstraction at all, as that's not what I am arguing: if anything, I am arguing that, due to the blub paradox, seeing the difference between the automation you encourage and the automation you have a distaste for is complex at best and impossible at worst; I look at your article and, as I have every other time I have examined Rust, am left wanting for more abstraction and automation when it comes to errors.
Your argument that you do not want error propagation to be implicit is strange to me, as a developer who is used to this being automatic, because it is does not affect safety to automate and is effectively nothing but boilerplate. There is nothing different in the form of your argument against exceptions from the form of the arguments made by developers of C or Go or Java against adding features like templates or optionals or macros. It isn't that Java developers hate abstraction in all forms (that would be an absurd argument that I would need to pull from an AbsurdArgumentFactory ;P), it is that the form of argument that we often hear from all of these developers about the features we like in languages like C++ or Rust or D has the same core structure :(.
On pcwalton's notes on exceptions, I already responded to him in my comment that started my involvement in this thread, and went further in my first reply this morning: his mental model seems stuck in the implementation of exceptions as defined by Java and as used by Java developers. His datapoint for why they are so slow is from an old story about gcj: the Java exception model requires a stacktrace reification, and you would not be able to replicate that kind of performance loss in C++, even though they share the same implementation of propagation. His API issues are all (as argued by others) based on the awkward way that Java developers have made exceptions used for things that aren't really errors.
Have you developed in Erlang? This is a language that considers errors and failures to be the core problem faced by developers, and which has structured almost all other decisions surrounding that core premise. After spending a lot of time coding in Erlang a few years ago, it fundamentally changed the way I think about errors, and that was after over 20 years of software development in languages like C++ and Java. When I was (comparatively) green and naive, back in 2001, I even made an argument for checked exceptions to Microsoft in C# (it was partly my argument for RAII in that same thread that got us the using syntax sugar for IDisposable): that is how far I had to come in the subsequent decade.
Erlang has the right abstraction: a mechanism for exceptions that is designed somewhat similarly to panic, in that it encourages you to structure your code in ways that isolate failures behind processes and avoid ever "handling" errors, but which lets you propagate and augment rich error context through multiple process layers implicitly, making error propgation automatic and without boilerplate. Once you "get it", you can do this easily in any language that has unchecked exceptions (even Java, using RuntimeException as the new exception root, but you have to really hate Java to pull his off ;P), as function calls can be mentally modeled as calls through process linkages (though you sadly then can't use the standard libraries provided by any of these languages, as they are all built with a broken model :().
Given how many other amazing things that Rust got uniquely right, it is somewhere between disappointing and devastating that error propagation, something which is so important, only seems to be competing with languages (like C, Go, or Java) where errors are almost unworkable :(.
There is no right abstraction. If we can't minimally agree that there are trade offs at play here, then we can't have a productive conversation.
> and avoid ever "handling" errors
I want to explicitly handle my errors.
Just because you had great internal progress does not mean you arrived at some objectively correct solution. It obviously works well for you, but everyone is not you and not everyone has the same requirements or preferences as you.
The arrow I am providing for progress is one towards abstraction and automation; so, fine: we are right back to where we were before, with you making an argument that is of the exact same form as those people from C, Go, and Java who argue against adding abstraction and automation to keep things explicit and transparent, the people you were so painfully opposed to being lumped together with, and you seem to have exactly as little concrete rational as they do for making that "tradeoff". It isn't clear there is any tradeoff in play here, and you haven't tried to demonstrate one.
My argument is "given that there is absolutely no demonstrated benefit to doing so, and even some clear downsides (performance of normal successful code), being forced to litter my code with tons of boilerplate--something you can clearly see if you glance almost anywhere in the code for Cargo--is a major and depressing step backwards". Your argument seems to still be "I don't want that much automation, I like things being explicit, and of course you should look again the comments from pcwalton you already responded to that complain about Java (really just Java) being slow"... :/.
Honestly, please just leave me alone and stop twisting my words. We can't even agree that there are trade offs involved here. There's no point in continuing.
I don't know Java or C++, so I can't help you there.
> I consider having to unwrap or try! on essentially every function call
They aren't on every function call. Only on functions that can return errors. The type signatures will tell you which.
> to be verbose.
I don't. In fact, I love how errors are handled. It's explicit, concise and ergonomic. We'll just have to disagree, I guess.