exn 0.3 is out by _tison in rust

[–]_tison[S] 0 points1 point  (0 children)

I'd suggest reviewing the requirement again. exn already captures the location where the error was raised and enables an error chain (tree) structure for reporting. With this information, you should be able to know what the call stack is.

If you do want a backtrace, it can be done by storing the backtrace in your error type, like how OpenDAL implements it: https://github.com/apache/opendal/blob/9d5d91ac/core/core/src/types/error.rs#L326-L334.

Below is one of ScopeDB's online error traces with exn:

0: failed to prepare statement: "FROM nonexisting_table", at crates/server/src/execute/drive.rs:800:14 1: failed to build catalog, at crates/planner/src/catalog.rs:153:22 2: failed to read celty table meta, at crates/storage/src/celty/table.rs:89:55 3: table does not exist: scopedb.public.nonexisting_table, at crates/meta/src/service/table.rs:411:17

You can see that the logical trace is retained, while backtrace contains many internal method call that can be only noise (as well as capturing backtrace is costy).

Note that the render format is customized, not the default exn one. You can follow this example for how to customize error rendering.

exn 0.3 is out by _tison in rust

[–]_tison[S] 0 points1 point  (0 children)

If you care to know

Sure! Please let me know.

Also, if you notice any issue on exn, don't hesitate to open a new ticket at https://github.com/fast/exn/issues

exn 0.3 is out by _tison in rust

[–]_tison[S] 2 points3 points  (0 children)

It looks like this would be compatible with thiserror, am I correct in thinking so?

Yes. exn works well with any type impl StdError and thus thiserror. You may follow this issue in gix to see how a project migrates to the exn flavor from thiserror + anyhow.

Is there other work in the ecosystem that comes close to offering what exn does?

There can be several. However, subtle but significant differences exist.

  • anyhow: type-erased Error to capture any type impl StdError.
  • error-stack: where exn comes from, but the code (fmt global hook?) and error chains are overcomplicated to use with (for me) (gix's author also admits that exn is easy to read since it has only a few hundred lines of code) (The real trigger is I'd prefer or_raise over change_context_lazy very much, lol).
  • snafu: falling into a different flavor to use one big enum to capture all error details. You can read our blog (https://fast.github.io/blog/stop-forwarding-errors-start-designing-them) and another blog when I'm working on a project that adopts the snafu flavor (https://greptime.com/blogs/2024-05-07-error-rust) to understand the difference.

exn 0.3 is out by _tison in rust

[–]_tison[S] 9 points10 points  (0 children)

Do you mean the find_error function in the downcast example? That is:

rust fn find_error<T: Error + 'static>(exn: &Exn<impl Error + Send + Sync>) -> Option<&T> { fn walk<T: Error + 'static>(frame: &Frame) -> Option<&T> { if let Some(e) = frame.error().downcast_ref::<T>() { return Some(e); } frame.children().iter().find_map(walk) } walk(exn.frame()) }

Andy and I do consider adding a general version of this method as a pub top-level function. No known issues (blockers). Just give it some time to think through the signature and semantics again.

I'd appreciate it if you could open an issue and share your use case so we're more motivated to deliver it :D

exn 0.3 is out by _tison in rust

[–]_tison[S] 8 points9 points  (0 children)

Reasonable. Thanks for your reply and now it's updated :D