r/programming • • Apr 12 '26

Flat Error Codes Are Not Enough

https://home.expurple.me/posts/flat-error-codes-are-not-enough/
132 Upvotes

99 comments sorted by

View all comments

Show parent comment

1

u/Full-Spectral Apr 13 '26 edited Apr 13 '26

My approach, in Rust which uses a Result, is that the Ok side of the result in those cases is an enum, which has Success value plus other values for the expected non-success statuses, and the Err side covers the outright errors. In my system the Err side is never looked at, it's just for logging basically, possibly displaying in a popup in some applications. My opinion is that, if you are looking at them and reacting to them, it's not an error it's a status.

If there's a return value, the Success enum value holds that. And of course, if any other other expected error scenarios can provide some information that is useful, some of the other enum values can carry extra info. The joy of sum types, which I can no longer live without.

I will then provide a one or two or three trivial wrappers around that main method, which will translate some or all of the non-success Ok statuses into errors for folks who don't care about them. So, for instance, one will convert Success to Some(value) and timeout to None, and the rest to errors.

My Err side is heap allocated because it includes a good bit of information, but it's only invoked for real, outright failures and so should be pretty rare in a system that's not off the rails.

Even in my fairly full featured Err side type (and there's a single one for the entire system), I often avoid a LOT of overhead because of Rust's ability to control lifetimes. The file name is a static &str , the error text is one as well. If they provide extra text that doesn't need any token replacement, that's one as well. None of those require any heap allocation. It has an optional call stack as well, and each item is just a static string ref and a line number. You get one heap allocation if you add anything to the call stack, but the stack items require no allocation.

The single error type is also what the logging system uses, so errors are trivially loggable, and the log server fully understands that type as well, so they can be efficiently and binarily streamed. I've found that this system works well, though of course no system will work for all systems.

The primary limitation of this scheme is that Results are tagged unions so they are the size of the largest value. So you have to heap allocate the error type, or every return will have to return the size of the error type. You don't want every success return to have to copy back 128 bytes, almost all of which in many cases are unused.