Errors in Rust can now be handled more ergonomically, cleanly, and simply: introducing a new error crate

Cex author here.

The cex crate simulates of checked exceptions in Rust, which can do this in the following way.

In Cargo.toml:

enumx = "0.4"
cex = "0.5"
use enumx::export::*;
use enumx::predefined::*;
use cex::*;

#[cex]
fn do_stuff() -> Result!( () throws Err1, Err2 ) {
    ret!(());
}

#[cex]
fn do_other_stuff() -> Result!( () throws Err1, Err2, Err3 ) {
    do_stuff()?;
    ret!(());
}

The polyerror crate chooses the "error-per-function" strategy, which is adopted by some previous version of cex. The code above could be written as:

cex! {
    fn do_stuff() -> ()
        throws Foo(Err1)
             , Bar(Err2)
    {
        Ok(())
    }
}

cex! {
    fn do_other_stuff() -> ()
        throws Foo(Err1)
             , Bar(Err2)
             , Baz(Err3)
    {
        do_stuff()?;
        Ok(())
    }
}

The cex!{} macro will produce enum do_stuff::Err and enum do_other_stuff::Err for do_stuff() and do_other_stuff().

But I changed my mind and gave up the "one-error-per-function" strategy in newer versions of cex. Because I noticed a fact that in a library, series of functions may throw the same set of errors, so "one-error-per-function" strategy is only used to track the source of errors which can be done by cex's "logging" mechanism.

Now, the cex library supports even more functionality.

  1. You can call do_other_stuff() inside do_stuff() while propagating errors is still convenient. Notice that the former throws less errors than the latter.

    #[cex]
    fn do_stuff() -> Result!( () throws Err1, Err2 ) {
        do_other_stuff()
        .or_else( |err| #[ty_pat(gen_throws)] match err {
            Err3(_) => /* deal with this error */,
        })
    }
    
    #[cex]
    fn do_other_stuff() -> Result!( () throws Err1, Err2, Err3 ) {
        ret!(());
    }
    
  2. Checked exceptions can fallback as impl std::error::Error or any other traits.

    #[cex]
    fn do_stuff() -> Result!( () throws Err1, Err2 ) {
        ret!(());
    }
    
    fn downstream_api() -> Result<(), impl std::error::Error> {
        Ok( do_stuff()? )
    }
    

@john01dav I'm sorry to introduce cex in your crate's announcement, but many people seems to hate checked exceptions and did not want to discuss in my previous post.

2 Likes