Skip to content

Privatize fields on public types to enable post-1.0 evolution #146

Description

@TethysSvensson

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions