Readit News logoReadit News
_nalply · a year ago
> But, since good programs don’t panic, and neither do good programmers, it’s very rare that using unwrap or expect is actually the right thing to do.

I respectfully disagree.

The assert!() macro is a way to document invariants. It's a bug if a variant is violated. It shouldn't have happened, but if it happens, then there's nothing the user can do except reporting the crash.

The unwrap() and expect() method document the invariant that the None and Err() variants shouldn't occur.

It's fail fast.

You should use error handling only if users would be able to handle the errors.

Tell the end user that the file they wanted to open is not readable, for example. Tell the users of your library that an error happened, like a parse error of a config file. And so on. Tell the users what they can fix themselves.

A bug in your library or program, that's something different. Fail fast! And Rust panics are perfect for that.

joshka · a year ago
The easiest counterpoint to this is to think about a HTTP request. If your library is called by one route, and is known to fail in certain circumstances, this failure should not bring down the entire system. A well designed library should generally not make the choice to crash a system - that's the caller's decision. Yes there are exceptions to this, which is why TFA stated this as "rare" not "never".

Using the assert macro in your code is (in my experience) generally bad. If your code is written well, you can never test that code path. Document invariants with tests instead, or better yet with infallible code.

zarathustreal · a year ago
I think you’re misunderstanding the “fail fast!” advice. You want to fail fast in development not in production. In production you want maximum robustness. Users would rather see an error saying “whoopsie, maybe try that again later” than for the program to exit. That’s part of the reason why the functional error handling patterns are becoming so popular these days. They force you to handle errors and give you type-level info about how the program can fail.
Buttons840 · a year ago
I want my programs to fail fast in production too, because it makes it less likely that even bigger problems will arise. There are many problems that are much worse than a program crashing.

Deleted Comment

edflsafoiewq · a year ago
You can catch panics.
Animats · a year ago
Maybe not perfect, but it seems to work out better than exceptions. Exceptions are a good idea which turned out to be too complicated.

A language has to use destructors to clean up for almost everything for this to work. "?" has no "catch" clause within the function. So if an object has an invariant, and that invariant must be restored on error, the destructors must restore the invariant. If that just means unlocking locks, closing files, or deleting data structures, that works. If some more complex invariant needs to be restored, "?" isn't enough. The calling function has to clean things up. This usually means encapsulating the function that does the work in a function that handles errors and cleanup. Basically, a try block.

hikarikuen · a year ago
This recent post resonated with me: https://cedardb.com/blog/exceptions_vs_errors/

There are certain obvious (and some less obvious) benefits to both exceptions and results, but I get the impression a lot of programmers have overreacted against exceptions.

Exceptions "just work" the same in every codebase and require little boilerplate in most languages. I think results really shine for internal business logic where errors are more "invalid" than "exceptional."

surajrmal · a year ago
That's what panics are for. They compile to the same code c++ exceptions would. You can unwrap your results to turn them into exceptions.
divan · a year ago
"Go’s Error Handling Is Perfect, Actually" [1]

https://blog.verygoodsoftwarenotvirus.ru/posts/errors-in-go/

Buttons840 · a year ago
> Spend any amount of time in programming circles, and just as the sun rises and falls, you are certain to hear someone complain about error handling in Go. These complaints are, anecdotally, rarely well thought out suggestions on what error handling could or should be like in a language like Go, but often merely boil down to “I don’t like having to look at it”.

I read it. The first paragraph dismisses preferences in a matter that boils down to preference.

> Note that for any sufficiently complex program that invokes many dependencies, this stack trace will be so far down the chain that you may not even see where you’re making the call that causes it.

I don't understand this part. Why would my code not appear in the stack trace? Did this author know how to read stack traces?

danenania · a year ago
Personally I find it much faster to pinpoint errors in the Go style.

With a stack trace, I have to cross-reference with code (ensuring versions match) and filter out a bunch of irrelevant calls in the stack. It’s not uncommon for the stack trace to end deep in library code with the root cause being many calls removed, making me check through a bunch of call sites to figure out what happened.

In Go if good context is added to errors, an error log is generally enough on its own to make it obvious exactly what went wrong.

scubbo · a year ago
This is a very long post to say "Go's Error Handling would be better if it was like Rust's, but the language designers made a mistake early-on and now we don't want to fix it"
tialaramex · a year ago
I understand that writer's position that they can't go back and fix it now, but in my mind "We can't very well fix it now" is quite different from perfect. I think such a rationale justifies every choice as equally "perfect" and is thus useless.
pjmlp · a year ago
Except for anything more production quality, one needs to lean on third party crates to compose errors without explicitly write tons of boilerplate composing result types.

Something that should be supported directly.

spoiler · a year ago
I agree that it's not ideal, but using something like anyhow/l and thiserror honestly doesn't feel _that_ bad.

> Something that should be supported directly.

Rust has "adapted" some crates into stdlib in the past. Are there any efforts to that for error handling?

steveklabnik · a year ago
We're on the like, third (fourth?) generation of error handling crates, and while there's some degree of consensus happening, I'm not sure that it's time for it yet.

If Rust had adopted error_chain into the stdlib, that would have been a huge mistake.

pjmlp · a year ago
So far only the related traits.

That is the kind of excuse we give in C and C++ land, using static analysis isn't that bad.

WorkerBee28474 · a year ago
For those who want to experiment with this style in C#, I've found this package to work: https://github.com/JohannesMoersch/Functional
Yukonv · a year ago
Another good option I’ve personally used if you want a smaller API surface with just Result and Maybe concepts is True Myth. https://true-myth.github.io/true-myth-csharp/
blastonico · a year ago
Isn't it based on ML family? I mean, I see Rust error handling heavy inspired in monads used in languages like OCaml and Haskell.

Is Rust doing something different?

saurik · a year ago
Rust's error handling isn't at all like using a monad. The entire point of being able to express the monad for something like the behavior you expect from error handling is that you write code which automatically propagates the error and in the same breadth prevents you from ever being able to see the error. The result is essentially exactly exceptions: you program the happy path and it entirely hides errors from you, as that's the point of the monad, which for this purpose you can pretty much conceptualize as programmatic flow control (or, even more crudely, an overloaded semicolon / statement separator operator).

Rust is using the data structure, but doesn't have a way to express or use the monad, so you have to deal with and manually propagate the error. But like, if you do not have the monad, the data structure is just awkward... the only reason the data structure for this monad even exists at all is to support the monad, and the reason for the monad is to get a syntax similar to exceptions! In a language with a ton of hard-coded syntax for all of these things you'd use a monad for in Haskell--whether it's error handling, asynchronous execution, scope allocation... whatever floats your boat--you should just use exceptions.

tialaramex · a year ago
You can choose to work with Rust's Result in a monadic way, that's what methods like Result::and_then and Result::or_else and so on are for.

Because it's just another type you could also do whatever else you like, unlike with the Exceptions in typical languages which have them where too bad, we bolted the information to the control flow so now we're going on a journey.

If you want to bolt control flow to some information in Rust that's fine, feel free to define a function which returns ControlFlow::Break for success if that suits you, the try operator understands what you meant, early success is fine. Actually you can see this reflected in the larger language because break 'label value; exists in Rust unlike for example C++.

pjmlp · a year ago
At least Haskell and OCaml also offer exceptions as alternatives.
IshKebab · a year ago
I don't know if I'd say it's perfect. I'd still like a way to break on `Err` in debuggers for example.
XlA5vEKsMISoIln · a year ago

    .site-page.loading { opacity: unset; }