4 ms·
> When you call a Rust function that can fail, do you carefully think through all the various failure paths and reason about everything from scratch? From scra
by couchand 7y ago
> When you call a Rust function that can fail, do you carefully think through all the various failure paths and reason about everything from scratch?
From scratch? No, of course not. The type of the function tells me just what errors I need to expect.
It's really not complicated. Either you don't care about the error (or it "shouldn't ever happen"), in which case you .expect("some message") and call it a day, or else you're writing professional code and you absolutely need to think about the error cases first.
> Exceptions free program logic on the common path from the mental overhead of the error path, and that's a good thing.
This is backwards from the correct way to write professional software. It is, unfortunately, quite common. The proper way to engineer bug-free software is to address the error cases first.
Making a habit of ignoring what errors might happen is a sure way to get exploitable vulnerabilities and generally unreliable software.
- quotemstr 7y ago> The proper way to engineer bug-free software is to address the error cases first. And that's what exceptions do: they handle the error cases in a uniform and automatic way so your program can focus on the logic paths. In the vast majority of cases, you "address" the "error cases" in a given stack frame by cleaning up and propagating all errors to your callers. Both exceptions and Rust's "?" operator automate this process: C++ exceptions just do it better. Don't conflate not handling errors with letters errors propagate automatically. Exceptions "address" all errors in a uniform way without polluting the logic with irrelevant details of the errors themselves. Generality and cleanliness are hallmarks of good code. When a language feature allows the right thing to happen by default, that's a good language feature. > professional Are you suggesting that anyone who uses exceptions is an amateur? Billions of dollars say otherwise. People write "professional" exceptional code all the time. Don't try to present your position as the only "professional" one.
- couchand 7y agoI suppose Joel does a better job explaining it than I do, so I'll just quote him verbatim. "In other words, the more information about what code is doing is located right in front of your eyes, the better a job you’ll do at finding the mistakes. When you have code that says dosomething(); cleanup(); "… your eyes tell you, what’s wrong with that? We always clean up! But the possibility that dosomething might throw an exception means that cleanupmight not get called. And that’s easily fixable, using finally or whatnot, but that’s not my point: my point is that the only way to know that cleanup is definitely called is to investigate the entire call tree of dosomething to see if there’s anything in there, anywhere, which can throw an exception, and that’s ok, and there are things like checked exceptions to make it less painful, but the real point is that exceptions eliminate collocation. You have to look somewhere else to answer a question of whether code is doing the right thing, so you’re not able to take advantage of your eye’s built-in ability to learn to see wrong code, because there’s nothing to see." https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...
- layoutIfNeeded 7y agoDon’t write C++ like C and cleanup will be guaranteed by RAII.
- couchand 7y agoYep, RAII is an eminently reasonable way to address the narrow question of cleanup. In fact, that's how idiomatic Rust code would handle it, too. That still leaves the general question of how to tell what errors a function may alert you to. Sanity suggests including it in a type signature, either in the return type or with checked exceptions. From what I've seen, the much more common solution in exception-using code is to ignore or forget the possibility of error.
- quotemstr 7y ago> Sanity suggests including it in a type signature, either in the return type or with checked exceptions Yet basically all of Rust's std::io propagates just io::Result<T>, which is Result<T, io::Error>, where io::Error is a generic result that could be anything vaguely related to the outside world. It's as useful as writing "throws Exception" in Java. C# gets along fine in practice using unchecked exceptions everywhere. Your point would be stronger if, say, File::Create specifically said it could fail only with things like disk-full, file-exists, and so on. But all it actually says is "this can fail". The information richness you're describing does not exist in practice.
- roblabla 7y agoThe problem with File::create is that it must interoperate with the existing OSes. And it turns out, File::create can fail for many many reasons. Your file can be on disk, it can be remotely accessible on the network, it can be virtual, etc... And the OS can return just about any error. Linux lists the errors returned on each operation, but they're really just advisory and not exhaustive. So for io::Error, we're basically constrained by what the OS provides. Not much Rust can do about it, I'm afraid.
- vbezhenar 7y ago> This is backwards from the correct way to write professional software. It is, unfortunately, quite common. The proper way to engineer bug-free software is to address the error cases first. I don't want to write a bug-free software. It required tremendous effort and even more tremendous effort to test every path (which usually is not performed, so people keep pretending that their software is bug-free, very few really do test everyting). I want to write software that works for its happy path and predictably fails for unhappy path. When that happens, I'll either rework the software adding relevant paths or fix the source of unhappy path.
- couchand 7y ago> predictably fails The issue is that, if you've not taken the time to consider the possibility of the error occurring, the failure is by definition unpredictable. It's predictable only if the caller 1) knows it can happen, which is often not the case in exception-using code, and 2) specifies the expected behavior, which is by design rarely the case in exception-using code.
- vbezhenar 7y agoWell, I'm writing Java code and exception can be thrown from literally every function call. It's hard to not expect it. It's easier to find code that never throws exceptions. It does not make it any harder to write. Basically you use try-finally for resource release and avoid long living mutable objects (which survive request for typical request-response style server) or write code extra carefully, so those objects don't stay in bad state, but those objects are extremely rare. So in the end it's not hard to write that code.
- quotemstr 7y agoPeople who write exceptional code consider the possibility of error all the time: there's just no need to mark this constant background possibility with syntax in every possible spot. When exceptional code calls a function, the programmer is saying that the default error behavior --- unwind and propagate to the caller --- is the right thing. It's the right thing in the overwhelming majority of cases, and it's so common that we don't need special syntax to say so. It's as if you're saying there's no difference between using an exception and not "consider[ing] the possibility of [an] error occurring". The lack of special syntax for this default behavior is not the same thing as error sloppiness. Rust's "?" operator does the same damn thing, except that you need to type a character on your keyboard to say so. With respect to the standard error contract, "?" in Rust functions as an "I agree" button. And we all know that people's eyes glaze over when prompted with endless "I agree" buttons and they just click "yes". "?" in Rust is the "I consent" button on a cookie warning popup.