Skip to content
This repository was archived by the owner on Sep 12, 2024. It is now read-only.
This repository was archived by the owner on Sep 12, 2024. It is now read-only.

Proposal: Reporter inversion #53

Open
@seanmonstar

Description

@seanmonstar

I would like to propose the idea of inverting the way error reporters generally work in Rust. Instead of a reporter iterating all causes of an error to format them, pass in format rules to the top-level error.

The problems I'm trying to solve are:

  • Default behavior should be most useful for people who don't know it could be more informative.
  • Errors that don't expose their source don't know what context they are being formatted in.

Problems

Default to most common case

I posit that the most common case that users print errors is for log purposes. I'll use an example of code I've seen over and over:

task::spawn(async move {
    if let Err(e) = do_it_better().await {
        warn!("did it worse: {}", e);
    }
})

The current best practices hopes to educate users who have done this to either change the error type they've been returning at this top-ish level tasks, or to remember to wrap the error in a reporter right before printing it.

I suggest that, instead of relying on education, the best practice simply assumes most users won't know about reporters. This change makes it so the common case simply prints all of the error chain to their logs. Specifically, separated by a : , not with newlines.

Once a user is educated about reporters, they can opt-in to customizing further, such as choosing newlines over colons, or how many down the stack to go, or whatever. When opted into a reporter, the reporter can then tell the Display of the error "I only want the top error plz".

As prior art, Go's simple way to wrap an error includes displaying it together on the same line, while also being available from errors.Unwrap.

Opaque errors don't know their context

Some libraries may wish to wrap errors in an opaque way, such that they don't expose the source so that users don't depend on a specific error. This is common in other languages, when it's preferred to not couple the internal details to the error, while still wanting to be to share the error messages to help debugging. hyper is one library with that desire.

Since these opaque errors don't include the wrapped error in source, the current reporters cannot access them. But since no context is provided to Display, the opaque error cannot try to mimic the output of the rest of the stack.

Solution

The format traits already include a way to inject some context: the std::fmt::Formatter. That's how a type can check f.alternate() or f.fill(). Some of those are unused by structs, which we could overload. Or, we could define new ones.

I outlined overloading some existing format flags a few years ago. My opinion about some details has changed since then, mostly that I believe the default should be to print everything in a single-line friendly mode, since as I described above, that's what I think the most common case is.

I appreciate that the proposal allows specifying how deep down the stack to print. It also uses f.alternate() to determine if it should fit on one line, or use newlines and a stack trace. If we created new flags, we could inject a mode or even "separator" characters, similar to f.fill(). However, the exact details are a bit of a bikeshed, and what I want most is simply to have the idea of inverting the reporters to be considered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions