Useful effect systems allow the end user to decide where and when to have the compiler enforce errors are handled. Nim has had an effect system for a while but became much more useful when `forbids: [IOError]` was added. It makes it easy to ensure certain type of errors are handled at specific points.
More languages should embrace effect systems. Ocaml's is even used to implement multithreading support, albeit effect systems vary widely in design and theory.
But errors-as-values are all the rage today. But modifying types, especially every type in the chain, is annoying and overly manual IMO.
Checked exceptions were annoying because you had to manually annotate the exceptions all the way up your chain. The list of effects should be generated by the compiler, and the IDE should show them when desired. Maybe manually annotated at external API boundaries which double as forcing the API dev to handle unlisted exceptions.
Acknowledging the error is handling the error even if partially. The point of exceptions is to only acknowledge error that one can/knows how to properly handle
In Rust this takes a single character. There's effectively no cost to having the programmer acknowledge the error, and there's a large benefit in that you now know that there's no such thing as an error that the programmer ought to have handled but was simply unaware of. That's a huge benefit for writing resilient software.
Alternatively everything just gets put into a `dyn trait` and you're effectively just bubbling up errors just like with exceptions, but with way more programmer overhead. The performance overhead of constantly doing if/else branches for errors adds up as well in some situations.
Of course a fair bit of Rust code just uses `unwrap` to deal with inconvenient errors.
All functions are capable of producing an unbounded set of errors.
(Yes, programming is hard.)
Yes and I'm opposed to such "if err != nil" boilerplate.
That is not what Rust boilerplate looks like.