# except **Repository Path**: jmjoy/except ## Basic Information - **Project Name**: except - **Description**: For Rust, a better `try/catch`-like error handler rather than `result`. - **Primary Language**: Rust - **License**: MulanPSL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-10-14 - **Last Updated**: 2023-11-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Except For Rust, a better `try/catch`-like error handler rather than `result`. # Why The official error handler for unwind-able error is `Result`, but why there is `panic`? Because `panic` is generally should not be handle, but why there is `catch-unwind`, because sometimes `panic` must be treaded. So why not merge them to one way, just like `Java` and `PHP`, to a better `throw` and `try-catch` process. ## Not good of Result - Merge business logic and error handle logic, but now there is `?`, hack-ful but can solve this problem. - No backtrace, it is painful for us to debug the program both development and production, now there is [fix-error rfc](https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md) or `backtrace` crates, but take time to implement. - No unified struct type of `Error`, it is painful for me to handle a lot of Type cast, otherwise there is `failure` and `error-chain` crates, but both unstable. - The worse, you can't return a error type not suite the function Type declaration, like checked-exception. This is sometimes make us crash, so we have to use `unwrap` or `except` to handle error, this is not runtime-safe, and also forced obsessive-compulsive disorder. Although we can `catch_unwind` the `panic`, but official not recommended. # Roadmap Rather than `Result` or `checked exception` or `dynamic exception`, I will implement a compile-time auto-generated enum Type for every catch, just like closure do, for better `try-catch`. Now just some thinking: - Provide macro `except::throw!` to throw a Exception, expand to `panic` a struct contains `code`, `message`, `Error`, `Backtrace`, etc. - Provide macro `except::try!` to wrap a code block, and `catch_unwind` a auto-generated enum, which is `must_use` and composed by all struct throws by `except::throw!`. - Provide macro `except::catch!` to match the enum auto generated by `except::try!`. # The best idea In my heart, the best idea of error handling is throw a `exception` rather than return a ADT such as `result`, but neither `checked exception` or `dynamic checking exception`, is the `compile-time auto deduction exception`. I don't know is there a similar theory about it, but I think it is cool. ## how it works? Let us look a piece of code: ```rust arr.map(|item| do_something(item)); ``` This is a general functional operation of array. But in real world, error will happen any time, although during iterating, so `do_something` may not success, maybe result a error. But, it's sad that the `map` function only accept a closure which return a `()`, we can't return a `Result`! So, how can we do? 1. Turn `map` to `for`, we can return a `Result` solved this problem, but it's not functional. In some case, it isn't a iterator, we has no idea. 2. Use `unwrap`, `expect` or `panic`, and then we use `catch_unwind` to handle the panic, it's work well, but not suit the rust's philosophy. And let we jump out of `rust`, try `checked exception` and `dynamic checking exception`? 1. checked exception: not works, because it also needs function meta declaration. 2. `dynamic checking exception`: it works, just like `panic`, but it's runtime exception handling, we can't know the real type of exception during compile-time, must downcast the type of exception to detect what exception we met, so not suit the rust's philosophy. Now we explain about the `compile-time auto deduction exception`, I will wite some pseudo-code. The defined of `do_something`: ```rust fn do_something() { throw MyException::new("A little error"); } ``` Look just like `dynamic checking exception`, but when compile, it will expand to: ```rust enum __AnonymousExceptionBucket_A__ { MyException { message: String }, } fn do_something() -> Result<(), __AnonymousExceptionBucket_A__> { return Err(MyException::new("A little error")); } ``` So `throw` is just a syntax sugar of `Result`. And when calling the `map`: ```rust arr.map(|item| do_something(item)); ``` It will expand to: ```rust arr.map(|item| -> Result<(), __AnonymousExceptionBucket_A__> do_something(item)); ``` Not just the function meta declaration of `do_something` will change, but the caller `map` will change too. What about multi Exception type throws in a function? I think dealing `Result` type cast is so boring in rust, let we see how `compile-time auto deduction exception` works. ```rust fn do_something() { do_something_first(); throw MyException::new("A little error"); } fn do_something_first() { throw MyAnotherException::new("Another little error"); } ``` Expand to: ```rust enum __AnonymousExceptionBucket_A__ { MyException { message: String }, MyAnotherException(message: String), } fn do_something() -> Result<(), __AnonymousExceptionBucket_A__> { do_something_first()?; return Err(MyException::new("A little error")); } enum __AnonymousExceptionBucket_B__ { MyAnotherException { message: String }, } fn do_something_first() -> Result<(), __AnonymousExceptionBucket_B__> { return Err(MyAnotherException::new("Another little error")); } ``` How to handle these exception, it's also easy: ```rust try { // try will return a `Result`, because it's a sugar. arr.map(|item| do_something(item)); }.catch { // it's the syntax same as `.await`, behave like `.match`. MyException(message, ...) => { ... }, MyAnotherException(message, ...) => { ... }, // Not need to exhaust the enum. } ``` But if we want to catch a `Exception` not appeared, we will receive a compile error: ```rust try { arr.map(|item| do_something(item)); }.catch { MyException { message, ... } => { ... }, MyAnotherException { message, ...} => { ... }, NotKnownException { message, } => { ... }, // Not compiled! because is not an element of enum. } ```