| This article feels like someone trying to find arguments and justifications in favour of an existing opinion/bias. > Compare this to functional-style errors, where error handling is manual and super tedious. You have to explicitly check if the return value is an error and propagate it. No, you don't. You can easily convert a functional style error into an exception on the call site explicitly (e.g. `std::optional<T>::value` in C++, or `.unwrap()` in Rust), propagate via language features (e.g. `?` in Rust) or library abstractions such as monadic operations. The fact that you explicitly have to check the return value is a massive win in readability and ensuring that the "error case" was considered by the caller. That is paramount for the robustness of any codebase. > But there’s much more: Allocations can fail, the stack can overflow, and arithmetic operations can overflow. Without throwing exceptions, these failure modes are often simply hidden. Turns out there is a good use case for exceptions: exceptional and rare errors that cannot be reasonably handled in the immediate vicinity of the call site. Exceptions and error types can and should coexist nicely. > The classic example is syscalls, which usually follow C conventions. Yes, C error handling from decades ago sucks at providing information and context for the source of the errors. This is not an intrinsic issue with error return types, it's just another thing that sucks about C and is the way it is because it's old. > We parse an int somewhere, and an `IntErrorKind::InvalidDigit` bubbles up at the user. How is that a bad thing? Either the user provided the string that should have been parsed, therefore it's useful information for them to know why it failed to parse, or they don't care much, and they can explicitly decide to convert the error into an exception and propagate it upwards. Again, it forces the user to think about the error case, which is excellent! > The following examples use C++ code, which allows us to compare both versions like for like: [...] Now show a benchmark where the error rate is 50% or more. |
C++'s policy of "exceptions should be exceptional" isn't a good answer to this either, and it introduces a lot of ugly edge cases when writing code. For instance, you have to make the judgment call of whether to handle commonly-expected errors as return values or exceptions (eg. checking if something exists, and returning it if it does).
In many cases it is impossible to make the right call. For instance, a "file not found" exception is no big deal when your library is being used in a GUI app where user actions are relatively infrequent, but if your library is used in a high-traffic server or for batch processing, suddenly all those "file not found" exceptions are eating a lot of CPU.
In general, you simply can't predict when users of your C++ code might find a case where a code path spams exceptions, tanking performance.
This problem also exists in other exception-using languages like Python and C#[0], but these languages tend to be used for less performance-intensive purposes (especially Python) so it doesn't come up as much, and generally exceptions are used even for expected errors.
[0] https://stackoverflow.com/questions/891217/how-expensive-are... (C# exceptions are 30,000 times slower than return codes)