Hacker News new | ask | show | jobs
by _nalply 641 days 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.

2 comments

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.

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.
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.
It depends on the program. You don't want your whole web server to crash because of a small error in one route. You probably don't want your CAD program to instantly crash and dump an error to the console because of a divide by zero in the constraints solver.

Though in some cases like that I think it might be appropriate to use `catch_unwind()`.

If you're writing something safety critical like avionics flight control software, you probably don't want to crash in production either. I've also always interpreted "fail fast" as "Make defects obvious during development so they don't exist when you deploy to customers."
These are thought provoking counter examples.

So, on a micro-level, let's say I have a function that expects x to be a float between 0 and 1, and there's some math and logic built on this assumption. Of course, in development if this expectation is violated, we fail fast and loudly and then fix the problem. In production it's not quite so clear. But still, is it ever the right thing to ignore that an invariant is violated and just hope that things somehow work out?

> Make defects obvious during development so they don't exist when you deploy to customers.

I agree with this phrasing. From another viewpoint, I would say:

If your code checks an invariant, and that invariant is broken never (never!, not even in production) just continue on and hope that things will work out.

> But still, is it ever the right thing to ignore that an invariant is violated and just hope that things somehow work out?

Yes! Of course! In many situations it probably doesn't matter that the numbers go a little wrong, and that result will be better than crashing. In other situations it will be better to crash than to give junk results. As we already said, it depends on the situation.

You can catch panics.
I'm not sure that is something you can rely in general due to panic = abort, though to be fair that's more a concern for library developers since IIRC they don't control that particular setting.
Panics are unexpected and not the preferred mechanism of error handling.

Stick to Result<T,E>.

The GP said

> Users would rather see an error saying “whoopsie, maybe try that again later” than for the program to exit

To achieve this, you need to catch panics. Rust does not use Result for things the OP is talking about like asserts.

If you want to handle the assert cases, you should not use the asserts in the first place.
That isn't possible. The assert may not even be produced in code you control.

The reason panics/exceptions exist is it is too onerous to handle every possible error condition at all callsites (allocation failure and broken pipes are the famous examples), and it is not possible to enumerate all possible error conditions (unintentional programmer errors for example).

People have religious ideas about handling panics for some reason.