Skip to content

feat: add explicit Exn type erasure - #61

Merged
tisonkun merged 2 commits into
codex/relax-exn-marker-sizedfrom
codex/explicit-exn-erasure
Aug 24, 2026
Merged

feat: add explicit Exn type erasure#61
tisonkun merged 2 commits into
codex/relax-exn-marker-sizedfrom
codex/explicit-exn-erasure

Conversation

@tisonkun

@tisonkun tisonkun commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Depends on

Complete workflow

This PR makes type erasure useful specifically at boundaries that require one concrete callback error type while callback implementations retain their own typed errors:

use exn::ErasedExn;
use exn::Exn;
use exn::ResultExt;

fn storage_callback() -> Result<(), ErasedExn> {
    read_storage()             // exn::Result<_, StorageError>
        .map_err(Exn::erase)?; // erase only at the callback boundary
    Ok(())
}

fn run_callback(
    callback: impl FnOnce() -> Result<(), ErasedExn>,
) -> exn::Result<(), CallbackFailed> {
    callback().or_raise(|| CallbackFailed) // typed again above the boundary
}

The intended flow is therefore:

  1. keep concrete Exn<E> errors inside each module
  2. explicitly erase the root marker where a callback or delegate signature must unify unrelated error types
  3. immediately add a typed parent when the host incorporates that callback failure into its own API

The same boundary type also allows heterogeneous callback failures to share a collection and be aggregated with IteratorExt::raise under one typed parent.

API

  • add the public alias ErasedExn = Exn<dyn Error + Send + Sync + 'static>
  • add Exn::erase(self) -> ErasedExn
  • add Deref<Target = dyn Error + Send + Sync> for ErasedExn

ErasedExn gives this common boundary type a standard name without introducing a wrapper or changing its representation. Callers can still define domain-specific aliases such as CallbackExn when that improves their API vocabulary.

erase only changes the compile-time marker. It moves the existing Box<Frame> without allocating, preserves the entire tree, and keeps concrete frame errors available through runtime downcasting.

Deliberate omissions

  • no default type parameter for Exn; typed errors remain the default design
  • no From<Exn<E>> for ErasedExn because it makes IteratorExt::raise inference ambiguous between identity conversion and erasure
  • no std::error::Error wrapper or tree-to-source() policy; interoperability is independent from marker erasure

Open question

  • Is erase the right consuming-method name, or should it be into_erased / erased?

Validation

  • cargo x lint
  • cargo x test --no-capture
  • doctest covers the typed → erased callback → typed parent workflow
  • integration tests verify allocation reuse, runtime downcasting, callback retyping, and heterogeneous iterator aggregation

@tisonkun
tisonkun marked this pull request as ready for review August 24, 2026 10:59
@tisonkun

Copy link
Copy Markdown
Contributor Author

Temporarily closing and reopening to retrigger the required stack-aware CI checks.

@tisonkun tisonkun closed this Aug 24, 2026
@tisonkun tisonkun reopened this Aug 24, 2026
@tisonkun
tisonkun merged commit 7dd75cd into main Aug 24, 2026
12 checks passed
@tisonkun
tisonkun deleted the codex/explicit-exn-erasure branch August 24, 2026 12:15
This was referenced Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant