Two years ago I started the cex project to support checked exceptions in Rust. After eating my own dog food, I'd like to share some thoughts - with little code and more like an essay.
#[cex] pub fn foo() -> Result!( () throws Bar, Baz );
The function signature indicates that
foo() may produce
Bar error and
Baz error. Without checked exceptions, this information can only be found in
foo()'s API document, or by checking
Baz should not be considered as "internal details" if they appeared in API document.
The big enum way may be regarded as "a summarizing error type per crate".
The same: both provide concrete error types.
The difference: checked exceptions provide precise error types for each function, while
Result<T, TheBigEnum>can be aliased as
pub type Result<T, E=TheBigEnum> = Result<T,E>;.
Various errors may occur during execution of a function.
If regarded as errors in different stages of the function's computation, they shoud be organized as variants of a whole enum which is the function's Error type. We may call it error-type-per-function strategy. These error types are most specific.
If regarded as independent errors which are general enough to deal with and specific enough to report, they can be organized as checked exceptions. Logging could be enabled to specify the location where error occurred.
The same: both provide convenient mechanisms for propagating errors and attaching context.
The difference: cex requires extra annotations of error types, but does not require trait bounds
Library users can choose whether or not to use checked exceptions even library author has utilized checked exceptions in library API.
Library author could provide "a big enum as the crate error", not for library API, but for library users' functions, the errors of which are all from the library API.
Library author could
impl std::error::Error foreach error thus
impl std::error::Error foreach checked exceptions. Library users can use
Box<dyn std::error::Error>as the error type of their own functions.