The errors that a procedure can run into are part of its interface. Languages like Zig and Swift make that explicit.
The difference is that callers (even indirect callers) can register handlers to handle those errors without unwinding the stack.
Maybe the error handler makes it return a default value. Maybe it will deallocate a cache to free up memory in a memory-constrained situation. Maybe it purely ignores EOF or a FILE DOES NOT EXIST error where those do not matter.
I also have a special error handler for parsing. If the error is a parse error, it prints the parse error in standard format, with source file, line, etc. Otherwise, it passes the error on to the next error handler in the chain.
This helps me modularize my code by keeping the parse error concerns separate from error handling for everything else.
20 years go while programming java, I was struck by how simple and universally useful it is, to have any method that throws exceptions to be forced to declare them in the method signature, and all callers are then forced to either pass on the exception or provide a try catch that deals with it, making it almost impossible to have programd crash from un handled exceptions.
It was perfect, except for all the crashes from null variables back then. And my current favourite language Dart cuts null pointers by 99.99 percent by giving you null safety.
Checked exceptions and null safety, and suddenly programs crash like 90 percent less.
Now we must just figure out how to systematically eliminate index-out-of-bounds too, then 98 percent of program crashes are gone. All without writing unit tests and other costly rituals.
The worry I have with code like that is that instead of letting the program crash it often just swallows exceptions. This can lead to a lot of silent errors that one becomes aware of way too late, for example after a lot of data has been corrupted for hours or years.
The difference is that callers (even indirect callers) can register handlers to handle those errors without unwinding the stack.
Maybe the error handler makes it return a default value. Maybe it will deallocate a cache to free up memory in a memory-constrained situation. Maybe it purely ignores EOF or a FILE DOES NOT EXIST error where those do not matter.
I also have a special error handler for parsing. If the error is a parse error, it prints the parse error in standard format, with source file, line, etc. Otherwise, it passes the error on to the next error handler in the chain.
This helps me modularize my code by keeping the parse error concerns separate from error handling for everything else.