Skip to content

Separate error construction from diagnostic trait bounds - #565

Draft
yiyueawa wants to merge 2 commits into
shepmaster:mainfrom
opengensokyonet:codex/conditional-error-prototype
Draft

yiyueawa wants to merge 2 commits into
shepmaster:mainfrom
opengensokyonet:codex/conditional-error-prototype

Conversation

@yiyueawa

Copy link
Copy Markdown

Generic libraries may need to preserve a typed failure without requiring the underlying value to implement Error or be 'static. Currently, SNAFU’s construction and diagnostic requirements make this difficult without adding bounds to the public API or writing implementations manually.

This change allows construction independently of diagnostic capabilities. Generated Display, Error, and ErrorCompat implementations have their own bounds.

This follows the model used by common derives such as Debug, Default, and Ord: a generic type can derive a trait without requiring its type parameters to implement that trait at the type declaration. The derive adds the necessary bounds to the generated implementation instead, making that implementation available conditionally. Those derives can infer conservative bounds; the relevant principle here is where the constraints belong.

For example:

use snafu::{ResultExt, Snafu};

#[derive(Debug, Snafu)]
#[snafu(display("read failed"))]
struct ReadFailure<E> {
    source: E,
}

fn attach<E>(result: Result<(), E>) -> Result<(), ReadFailure<E>> {
    result.context(ReadFailureSnafu)
}

attach accepts borrowed payloads that implement neither Debug nor Error. ReadFailure<std::io::Error> still implements Error and exposes its source normally.

Changes

  • Remove diagnostic bounds from IntoError<E> and the corresponding synchronous, future, and stream context adapters.
  • Infer formatting bounds for directly referenced generic fields, according to their format specifiers.
  • Add type-level display_bounds(...), error_bounds(...), and error_compat_bounds(...) overrides. Each replaces inference for its corresponding implementation; an empty list disables that inference. Bounds declared on the type remain in effect.
  • Avoid circular predicates for directly recursive generic errors, including formatting and backtrace delegation.
  • Document the behavior and migration requirements, and add regression tests and CI coverage.

Compatibility and limitations

This includes breaking changes. Removing the bounds from IntoError<E> can break generic code that previously obtained E: Error + ErrorCompat from C: IntoError<E>. Such code must state the capabilities it uses explicitly.

Formatting inference is syntactic and best effort. Arbitrary expressions, such as a trait method call or transformation, may require explicit display_bounds. Formatting itself remains delegated to Rust’s formatting macros.

Source inference does not perform name resolution or arbitrary autoderef analysis. Aliases, custom pointers, and mutually recursive generic types may require explicit overrides. This can also require changes to existing definitions.

The generated Error implementation still requires Self: Debug + Display. Sources exposed through the standard error chain must satisfy its 'static requirement; this does not impose 'static on every context field. ErrorCompat remains independently available.

Constructors that generate source-aware implicit data retain the source capabilities required by GenerateImplicitData::generate_with_source. The implementation-bound overrides do not change those constructor requirements.

Validation

Local validation passed for:

  • Default and futures-enabled tests.
  • All 36 compile-fail cases and 103 documentation tests.
  • Rust 1.65 compatibility tests.
  • thumbv6m-none-eabi builds on stable, Rust 1.81, and nightly, covering the applicable alloc, futures, and provider API combinations.
  • Minimum dependency versions and the existing compatibility suites.
  • Formatting and documentation builds with warnings denied.

The nightly checks used rustc 1.100.0-nightly (feaadeeac 2026-09-19).

Clippy completed with existing warnings. CI now includes additional generic-error checks for MSRV, embedded targets, and the provider API.

Feedback requested

This is a draft to discuss whether construction and diagnostic capabilities should be separated this way, whether the per-trait override attributes are the right API, and how the compatibility changes should be introduced.

AI assistance

OpenAI Codex assisted with the implementation, tests, documentation, and validation.

@netlify

netlify Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for shepmaster-snafu ready!

Name Link
🔨 Latest commit 03cb8d2
🔍 Latest deploy log https://app.netlify.com/projects/shepmaster-snafu/deploys/6aafba73beafe00008bf77f9
😎 Deploy Preview https://deploy-preview-565--shepmaster-snafu.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

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