Introduce consistent typed error handling#85
Conversation
| ) -> Result<IMT, &'static str> { | ||
| if leaves.len() > arity.pow(depth as u32) { | ||
| return Err("The tree cannot contain more than arity^depth leaves"); | ||
| ) -> Result<IMT, ImtError> { |
There was a problem hiding this comment.
This is a breaking change since SMTError import path changes and InvalidParameterType variant shape change from tuple to named fields.
How should we approach this? @vplasencia
There was a problem hiding this comment.
For this what about we use #[deprecated(note = "Use new_with_error instead")] ?
There was a problem hiding this comment.
I think it doesn't apply here since you can't deprecate a variant shape change, and there's no new_with_error method?
Let's confirm with Vivian how to approach these kind of breaking changes
|
Besides the comments, there are a few minor issues related to the contribution guidelines:
Could you please squash these commits into something like: |
|
Thanks for the comments @0x471, it's more ergonomic now and I just squash all the changes in one commit |
|
|
||
| #[derive(Error, Debug, PartialEq, Eq)] | ||
| pub enum ImtError { | ||
| #[error("Too many leaves: got {got}, max for arity {arity} and depth {depth} is max {max}")] |
There was a problem hiding this comment.
not a blocker but there's a redundant "max" word here
|
Thanks for the PR, @protocolwhisper! Let’s sync with Vivian on how to handle the SMTError breaking change, and aside from that, this looks good to me to merge |
Description
This PR improves error handling across the IMT and SMT libraries by replacing ad-hoc or manually managed errors with explicit custom error types.
What kind of change does this PR introduce?
Refactor / API improvement
What is the current behavior?
In
imt, several public methods returnedResult<_, &'static str>, for example:newinsertupdatedeletecreate_proofThese methods returned plain string errors such as
"The tree is full"or"The leaf does not exist in this tree"instead of structured error values. Insmt, there was already anSMTErrorenum, but itsDisplayandstd::error::Errorimplementations were handwritten insidesmt.rs.What is the new behavior?
This PR introduces dedicated error modules and typed error enums:
ImtErrorincrates/imt/src/errors.rsSMTErrorincrates/smt/src/errors.rsFor
imt, methods now returnResult<_, ImtError>instead ofResult<_, &'static str>, with concrete variants such as:TooManyLeaves { got, arity, depth, max }TreeFullNotContainedFor
smt, the existing error handling is cleaned up by movingSMTErrorinto its own module and derivingthiserror::Error, replacing the manualDisplayandErrorimplementations. TheInvalidParameterTypevariant is also changed to a named-field variant for clearer context.Why the previous version was problematic
Returning
Err("...")from public APIs is less robust than using a dedicated error type. As described in Rust By Example: Defining an error type, custom error types are preferable when a function may fail in different ways, because they make failures explicit, easier to match on, and easier to extend with contextual information.That applies directly here:
&'static strdoes not let callers reliably pattern-match on error variants,got,max,arity, anddepth,Does this PR introduce a breaking change?
Yes.
This changes public return types in
imtfromResult<_, &'static str>toResult<_, ImtError>. Users who were matching on string errors or expecting&'static strwill need to update their code to handleImtErrorinstead. The SMT refactor may also affect users importing or referencingSMTErrorfrom its previous location.Other information
This change makes the libraries more idiomatic and easier to maintain by using typed errors with
thiserror, and by separating error definitions into dedicated modules. It also improves debuggability by attaching structured information to certain failures instead of only returning fixed strings.Checklist