refactor!: relax Exn root marker bounds - #60
Merged
Conversation
tisonkun
marked this pull request as ready for review
August 24, 2026 04:03
Contributor
There was a problem hiding this comment.
Pull request overview
This PR relaxes the Exn<E> root marker bounds to allow E: ?Sized where E is only used as a type-level marker, while keeping Sized requirements for APIs that accept or store values by value. It also removes an unused associated type from ResultExt to unblock the Result<T, Exn<E>> implementation from accepting unsized markers.
Changes:
- Allow
Exn<E>and related marker-only APIs/impls to useE: ... + ?Sized. - Update formatting impls and conversions to work with unsized root markers (
Display,Debug,From<Exn<E>> for Box<dyn Error...>). - Remove the unused
ResultExt::Errorassociated type (breaking change) and document it in the changelog.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| exn/src/result.rs | Removes ResultExt::Error and updates Result<T, Exn<E>> extension impl to accept E: ?Sized. |
| exn/src/iterator.rs | Relaxes the child root marker bound C in IteratorExt::raise to ?Sized. |
| exn/src/impls.rs | Makes Exn<E> accept E: ?Sized, moves marker-only APIs (raise, frame) into a ?Sized impl, and relaxes From<Exn<E>> conversions. |
| exn/src/ext.rs | Allows exn::Ok helper to use an unsized marker type parameter. |
| exn/src/display.rs | Implements Display for Exn<E> with E: ?Sized using the stored frame error. |
| exn/src/debug.rs | Implements Debug for Exn<E> with E: ?Sized using the stored frame. |
| exn-anyhow/src/lib.rs | Allows into_anyhow to accept Exn<E> with E: ?Sized. |
| CHANGELOG.md | Notes the breaking removal of ResultExt::Error. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
andylokandy
approved these changes
Aug 24, 2026
This was referenced Aug 24, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Exn<E>to use an unsized root error marker?Sizedonly through APIs and implementations that carryEas a markerSizedwherever an error, closure, iterator, or success value is accepted or stored by valueResultExt::Errorassociated type, which otherwise prevents theResult<T, Exn<E>>implementation from accepting an unsizedEMotivation
Exn<E>stores aBox<Frame>and usesPhantomData<E>only to describe the root error type. The root value itself is already stored behindBox<dyn Error + Send + Sync>inside the frame, so requiring the markerEto be sized is not a storage requirement.This relaxation allows downstream APIs to describe an unsized marker without committing
exnto a type-erasure API or name:It also makes a later type-erasure conversion additive: a follow-up can decide whether a standard alias and conversion API are useful without changing the structural bounds again.
Scope boundary
This PR deliberately does not provide a way to construct or convert into
Exn<dyn Error>. A downstream crate can name a local alias after this change, but cannot safely convertExn<ConcreteError>into it becauseExn's fields are private and Rust does not perform that marker coercion automatically.Type-erasure naming, conversion ergonomics, callback guidance, and a specialized
Derefimplementation belong in a separate follow-up if the feature proves worthwhile.The generic
Deref<Target = E>implementation also remains sized becauseError::downcast_ref::<E>()requires a sized target.Breaking change
ResultExt::Erroris removed. The associated type was declared and assigned by both implementations, but noResultExtmethod or implementation reads it. Downstream code that explicitly refers to<R as ResultExt>::Errormust stop doing so.Validation
cargo x lintcargo x test --no-captureRelates to #35.