Panic vs Result/Option

I question the value of the term 'exceptional' here. What is exceptional? In my experience, 'exceptional' means you can't predict it well enough to code around it, so I don't see any value in trying to code around it. You manage exceptional errors by having redundant systems and good tracing/analysis tools.

2 Likes

This is what we're discussing here. If you have a function, that accepts 2 slices and your function documentation says, that it expects 2 equal-sized slices, but you provide different-sized slices, is that exceptional? It definitely is a logic error and the only reason this isn't a compile time error is, because it is impossible to check at compile-time.

The remaining question is, how should this case be handled? For the function, it certainly makes no sense to continue with whatever it wants to do, but how it stops its exution is what's interesting. A logic error in the code is certainly a bug and trying to recover from a logic error might be triggering even more logic errors, that originate from the first one.

Shouldn't we just panic, then? It does make sense and in most cases it's the right thing to do, but someone here in this thread has a use-case where panicking should be prevented, whenever possible. Sadly, we don't know the specific reasons, but I made some guesses about that.

Memory leaks are common when panicking and if the system can and will recover from any error, it'd be much better, if the function would just return a regular error, indicating, that something terrible happened, but not leaking memory, because the drop code is run before propagating the error to the point where it can be handled.

EDIT: Maybe, it's better to label the errors as either expected or unexpected and reserve the meaning of exceptional for the few cases, that will most likely be impossible to handle, e.g. stackoverflow errors or when code can somehow figure out, that undefined behavior has already been triggered.

I just don't understand the extra terminology, (here or elsewhere.) It doesn't matter if you want to call them expected or unexpected, because that's entirely a function of the awareness of the programmer, not the state of the application.

Right now, we've got two main error handling mechanisms: Result, which encodes a local check & branch, and panic, which avoids the overhead of that branching by bringing down the whole application. Introducing additional concepts like "exceptional" or "unexpected" is only muddying the waters. Code has to compile into something, and everything it compiles into is to handle the expected and non-exceptional.

The remaining question is, how should this case be handled?

If someone is violating the invariant declared in your API, you should panic. Why? Because if you don't, you're punishing everybody who uses your API correctly with additional checks and branches. You should be asking users to sanitize their inputs, not doing it for them, for one big reason: they may already be sanitizing their inputs. Forcing them to do it twice is wasteful as well.

So if you return Result here, you are punishing people who sanitize their inputs (or already know they are sanitized), and you punish people for using your API correctly.

If that is still unacceptable to you, then you can also provide an alternative API that returns Result, and leave the panicking one in as do_blah_unchecked . There's no reason you have to do it only one way.

if the system can and will recover from any error

I never met a system that can do this, so please clarify?

Memory leaks are common when panicking

If a system can recover from a panic, then why can't it clean up memory too?

2 Likes

The "official" terminology is that panics are for "unrecoverable" errors. In practice, this means errors where the program cannot be guaranteed to be in a valid state, so we must either abort or unwind back to someplace where we know the state is valid.

If a system can recover from a panic, then why can't it clean up memory too?

In most cases, it can. Destructors are run during unwinding, which will free any memory owned by the unwound frames.

However, there are cases in unsafe code where, if a panic happens while initializing (or destroying) some value, we can't be sure whether it has been fully initialized (or fully destroyed). If it's not guaranteed to be in a state where it's safe to call its destructor, then it's necessary to err on the side of soundness and risk leaking it instead.

2 Likes

In those cases, I would expect that the constructor which is invoking the panic would make the best effort it can to clean up the stuff it has successfully initialized before it invokes it.

I was asking the person who brought this topic up the same, but haven't received an answer:

I am mostly guessing about the possible constraints, that might lead to wanting to have this flexible error handling. I can't tell you anything else than what has already been written in this thread. This could be a case where the software in question could cost lives, if it's not working properly and must be designed to be maximally error tolerant.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.