Goal
After 1.0, we want to be able to rename fields, change field types, merge or split fields, add validation, or make fields computed — without breaking changes. Public fields lock us out of all of that, not just "can't add new fields."
Approach
- User constructs, lib reads → builder with sensible defaults / presets.
- Lib constructs, user reads → accessor methods only.
- Public enums →
#[non_exhaustive] on the enum and on variants with struct-like fields, preserving user pattern-matching.
Audit (complete — anything else is already opaque, a ZST marker, or a frozen trait signature)
| Type |
Treatment |
Why |
DefaultReportFormatter |
builder |
25+ fields; keep ASCII/DEFAULT as const builder outputs |
BacktraceFilter |
builder |
5 fields; keep DEFAULT preset |
ContextFormattingStyle |
builder |
follow_source: bool + follow_source_depth: Option<usize> likely wants to collapse |
AttachmentFormattingStyle |
builder |
|
LineFormatting, ItemFormatting, NodeConfig |
builder |
likely to want owned/Cow strings for runtime-configured styles |
SpanCollector |
builder |
single bool today, but more knobs expected; user doesn't read it back |
Location |
accessors |
column already missing |
Backtrace, Frame, FramePath, FramePrefix |
accessors |
String fields likely want to become Cow or refs into a shared buffer |
AttachmentParent |
accessors |
|
HooksAlreadyInstalledError(pub Hooks) |
accessor (into_hooks) |
|
AttachmentFormattingPlacement |
#[non_exhaustive] enum + per-variant |
InlineWithHeader { header } / Appendix { appendix_name } variant fields likely to grow |
BacktraceEntry |
#[non_exhaustive] enum + per-variant |
|
FormattingFunction |
leave public |
Display/Debug plausibly frozen; #[non_exhaustive] would force .. on every user match for no realistic gain |
Opaque-struct-over-enum (considered, rejecting for now)
For AttachmentFormattingPlacement and BacktraceEntry we could go further and wrap the enums in opaque structs (fn is_inline() -> bool, fn appendix_name() -> Option<&str>). That gains representation flexibility but costs user-side pattern matching — the primary way users dispatch on these in formatter and inspector code. #[non_exhaustive] enums give us most of the upside (new variants, new variant fields) without that cost. Worth revisiting if a specific representation change ever forces our hand.
Out of scope
Trait method signatures (ContextHandler::preferred_formatting_style, etc.) are frozen the moment 1.0 ships regardless of this work. A separate pre-1.0 pass should enumerate signatures we want to revisit before locking — tracked in a follow-up.
Rollout
Land in one batch (or one PR per crate) so downstream user code churns once rather than across multiple 0.x releases.
Goal
After 1.0, we want to be able to rename fields, change field types, merge or split fields, add validation, or make fields computed — without breaking changes. Public fields lock us out of all of that, not just "can't add new fields."
Approach
#[non_exhaustive]on the enum and on variants with struct-like fields, preserving user pattern-matching.Audit (complete — anything else is already opaque, a ZST marker, or a frozen trait signature)
DefaultReportFormatterASCII/DEFAULTasconstbuilder outputsBacktraceFilterDEFAULTpresetContextFormattingStylefollow_source: bool+follow_source_depth: Option<usize>likely wants to collapseAttachmentFormattingStyleLineFormatting,ItemFormatting,NodeConfigCowstrings for runtime-configured stylesSpanCollectorbooltoday, but more knobs expected; user doesn't read it backLocationcolumnalready missingBacktrace,Frame,FramePath,FramePrefixStringfields likely want to becomeCowor refs into a shared bufferAttachmentParentHooksAlreadyInstalledError(pub Hooks)into_hooks)AttachmentFormattingPlacement#[non_exhaustive]enum + per-variantInlineWithHeader { header }/Appendix { appendix_name }variant fields likely to growBacktraceEntry#[non_exhaustive]enum + per-variantFormattingFunctionDisplay/Debugplausibly frozen;#[non_exhaustive]would force..on every user match for no realistic gainOpaque-struct-over-enum (considered, rejecting for now)
For
AttachmentFormattingPlacementandBacktraceEntrywe could go further and wrap the enums in opaque structs (fn is_inline() -> bool,fn appendix_name() -> Option<&str>). That gains representation flexibility but costs user-side pattern matching — the primary way users dispatch on these in formatter and inspector code.#[non_exhaustive]enums give us most of the upside (new variants, new variant fields) without that cost. Worth revisiting if a specific representation change ever forces our hand.Out of scope
Trait method signatures (
ContextHandler::preferred_formatting_style, etc.) are frozen the moment 1.0 ships regardless of this work. A separate pre-1.0 pass should enumerate signatures we want to revisit before locking — tracked in a follow-up.Rollout
Land in one batch (or one PR per crate) so downstream user code churns once rather than across multiple 0.x releases.