| > Result is for expected domain failures. Panics are for programmer errors and unrecoverable constraint violations. The problem is that "unrecoverable constraint violations" happen a lot in practice when you're dealing with filesystems, networking...anything that isn't pure computation. Suppose I have a function that calls other functions that themselves make 3 database queries, two HTTP requests, and reads/writes from a cache directory. It considers all of them (except perhaps the caching) unrecoverable in the context of that function. What should it do? I see three reasonable options: (1). return a simple error type saying "Networking failure", "IO Error", etc if any of those fail (2). return a complex error type that exposes the internal details of all the different things it's doing and which one failed and why (3). panic if any of them fail I would argue that (1) is unfit for purpose as you have no idea what's actually going wrong. And (3) is currently very heavily discouraged, though I think if I'm understanding your argument right it probably makes the most sense. However it leaves your top-level function in the awkward position of needing to make that panic part of its API contract, without the type system to help. It's also highly limiting because the caller now can't distinugish between programmer errors and possibly-transient environmental conditions like a service outage. (2) is what I'd expect to see in practice right now, and that's what leads to these automatic stack traces, etc. But none of these feel like good options. Ideally I'd want something that is: - Debuggable (like (2) and (3)) - Part of the type system (like (1) and (2)) - Still allows introspection by the caller (like (1) and (2)) - Doesn't require a ton of boilerplate at each level (like (3), and possibly (1)) (edited for formatting) |
"Unrecoverable constraint violations" occur, for example, when you've done a sanity check on some data structure and found that it's in a state that should be impossible, and so continuing from there is unsafe.
Even then, you may choose to handle them in a better way than simply aborting the program. For example, if I'm writing a HTTP service that is backed by a database, and I get a customer request that results in me finding that a column in the database is NULL when it shouldn't be, I'll probably just return a 500 error to the customer rather than panic!(). The assumption is that even though there's a problem with this particular data, that might be the result of an almost-never-hit edge case, and we can still serve other customer requests just fine.
Sure, a simple single-user command-line application may choose to panic!() if a critical data file can't be opened from the filesystem. Maybe that is an "unrecoverable constraint violation" sometimes. But I think there's a lot of nuance you're missing.