Absolutely you should return an error. Whether the caller wants to panic or handle it or print unicorns should be left up to the caller, not your function. Functions should not be expected to tear down the thread in case of an error. Nothing that panics should belong anywhere in exported code
* Index out of bounds on every array op
* Integer overflow on every arithmetic op
* OOM on every allocating op
Maybe if you're writing an ironclad RTOS for a critical system without any good redundancy? Otherwise these are such pervasive operations that most have accepted that they're not worth handling everywhere they happen. Requiring that really dilutes the value/meaning of errors.
Given this signature:
fn do_thing() -> Result<T, E>
There's a clear signal that there's legitimate error conditions that you probably want to think about today. If every function that accessed arrays, worked with integers, or allocated memory returned a Result, it would border on meaningless. It would be like if there was no Throwable/Exception distinction in Java. Everything would `throw Exception`, eliminating the value of even noting that something can fail.
I think the only way such a system could be tolerable is if the language in question had really good dependent type support (but that wouldn't handle the OOM issue -- which you can't even reliably handle on some systems).
Unwinding/crashing is valuable in a truly robust system, because it needs to handle crashes anyway. Might as well punt obscure problems to that level of the reliability system. (This is basically the basis of Erlang's task system, AFAIK)
No. You have already diluted the meaning of errors, and you want them elevated to _your_ standard.
>Index out of bounds on every array op
These are removed if you build a rust program with --release.
>Integer overflow on every arithmetic op
Add the Wrapping class if you expect overflow. Overflow _shouldnt_ normally happen on an Integer operation. It is a hardware error when it happens, and can cause massive pain-in-ass bugs when it happens unexpectedly.
I'd rather get errors when it does happen, rather then find out 6 months into a production run.
>OOM on every allocating op
C does this also.
Rust doesn't remove bounds checks in --release. It's wrapping that gets turned on in release. I'm not sure why you're distinguishing overflow as truly exceptional, as opposed to any other "this should never happen" error?
Also I don't think many C libraries that allocate expose that as a failure condition (I've certainly seen some which don't even check!)
This is a very common mistake in most C code. Malloc can fail, and it return NULL when it does.
Most errors are recoverable. panic! is an antipattern in a library. Or at least, provide both a panic-ing and a non-panic-ing variant.
We're actually in the midst of arguing whether to violate this convention and do it for RefCell: https://github.com/rust-lang/rust/issues/27733
Using assert! to verify that your state is consistent is useful, in case you have a bug. But since it's all internal, it's not something that the caller can really recover from, either. It's not their fault, it's yours.
Worst of all, it leads to untestable code, because the calling test case can't properly check the error against some known result.
That said, I think you're too dismissive of "can never fail" assumptions. There's tons of places where something is optional in general, but based on various invariants you know it won't be. For instance, if you successfully acquire the first element in an array, you know you can get the last.
If you legitimately believe that Option is always Some, I don't understand what you think you would otherwise put there? Gonna have your function return an Err if an internal invariant is broken? Gonna have callers actually check for that?