snafu icon indicating copy to clipboard operation
snafu copied to clipboard

Include `Location` in `snafu::report` / `Report`

Open bgrieder opened this issue 7 months ago • 2 comments

When errors include Locations as in this example, it would be nice to specify the Location property name in the snafu::report proc. macro e.g. #[snafu::report(location(location))], so that the location gets printed with the errors.

#[derive(Debug, Snafu)]
pub enum EngineError {

    #[snafu(display("{message}"))]
    UrlParse {

        message: String,

        source: url::ParseError,

        #[snafu(implicit)]
        location: snafu::Location,
    },
}

would print

Error: Unable to frobnicate the mumbletypeg

Caused by these errors (recent errors listed first):
  1: Could not contact the mumbletypeg API
  2: The URL could not be parsed (/path/to/file.rs:123:45)

Note 1: all Location properties need to be named the same, but that is a reasonable constraint Not 2: I know backtrace is available, but it is expensive to collect

bgrieder avatar May 31 '25 10:05 bgrieder

This is not possible today, but I can see a path to supporting this on unstable Rust.

Right now, Report does not show backtraces in stable Rust. It requires access to the unstable Provider API, which allows associating arbitrary data with an error.

For this to work, we'd need to expose the Location from each error. I think we also have to make it such that provided values are not chained. Then we'd have to extend Report to handle this. The error definition would look something like this (hypothetical syntax).

#[derive(Debug, Snafu)]
pub struct InnerError {
    #[snafu(implicit, provide(chain(false)))]
    location: snafu::Location,
}

#[derive(Debug, Snafu)]
pub struct OuterError {
    source: InnerError,

    #[snafu(implicit, provide(chain(false)))]
    location: snafu::Location,
}

It'd also be a bit unfortunate that Report would gain SNAFU-specific knowledge / implementation. There's also the problem that there's no ecosystem-wide guidance for how to include location information. For example, we avoid including the source error text in a wrapper error's message, but there's no equivalent for location information. That could mean that people would do something like

#[derive(Debug, Snafu)]
#[snafu(display("Bad thing! (occurred at {location})"))]
pub struct Error { /* ... */ }

Which would lead to Report double-printing it.


#[snafu(display("{message}"))]
UrlParse {
        message: String,
}

FWIW, I strongly discourage this style of error. Favor more specific error variants instead of cramming all the errors into one variant and then distinguishing them with an ad hoc string.

shepmaster avatar Jun 02 '25 13:06 shepmaster

Thanks.

FWIW, I strongly discourage this style of error Yes. This was just for illustration. My objective is to get locations in reports.

This post effectively describes the goal and may serve as a source of inspiration.

https://medium.com/@greptime/error-handling-for-large-rust-projects-a-deep-dive-into-5e10ee4cbc96

They implement a proc macro #[stack_trace_debug] and decorate all Errors with it.

They also have a benchmark section on the use of Location compared to Backtrace, which shows little overhead in using locations.

The resulting report is quite nice:

Failed to handle protocol
0: Failed to handle incoming content, query: blabla, at src/protocol/handler.rs:89:22
1: Failed to reading next message at queue 5 of 10, at src/protocol/loop.rs:254:14
2: Failed to decode `01010001001010001` to ProtocolHeader, at src/protocol/codec.rs:90:14
3: serde_json(invalid character at position 1)

bgrieder avatar Jun 03 '25 05:06 bgrieder