anyhow icon indicating copy to clipboard operation
anyhow copied to clipboard

[Question] In `{:#?}` why is the outermost context called "caused by" and how is `Context` expected to be used?

Open nathan-at-least opened this issue 1 year ago • 1 comments

Suppose I have code like this (see this playground):

use anyhow::Context;

fn my_thing(path: &Path, extra_sauce: bool) -> anyhow::Result<()> {
    std_or_some_external_lib::do_other_thing()
        .context(format!("path: {:?}", path.display()))
        .context(format!("extra_sauce: {:?}", extra_sauce))?;
    Ok(())
}

Let's suppose do_other_thing results in an error which displays as failed to do other thing because <reason>

Now what I intend by the code above is "if do_other_thing results in an error, then when describing that error cause to a user, provide this extra context for the user to have more context about the cause", so I would expect to see this kind of output:

Error: failed to do other thing because <reason> 

Context:
    0: path: "/tmp/non-existent"
    1: extra_sauce: true

But for some reason the {:#?} display calls the outermost "the cause" so it shows this output:

Error: extra_sauce: true

Caused by:
    0: path: "/tmp/non-existent"
    1: failed to do other thing because <reason>

This output seems backwards and confusing to me. Am I somehow misunderstanding Context? How should it be used?

It feels like it needs to be used like this in order to have clearer error messages, which seems clunky and redundant:

use anyhow::Context;

fn my_thing(path: &Path, extra_sauce: bool) -> anyhow::Result<()> {
    std_or_some_external_lib::do_other_thing()
        .context(format!("path: {:?}", path.display()))
        .context(format!("extra_sauce: {:?}", extra_sauce))
        .context("failed to do some other thing".to_string())?; // Why am I describing the error of `do_other_thing` which already describes itself?
    Ok(())
}

nathan-at-least avatar Oct 26 '23 18:10 nathan-at-least

Based on my rereading of Context#example it seems like I need to retrain myself to rename Context::context as Context::caused_by

In other words, arguments to that method should always describe a cause of an error, but not "auxillary context the user should know about the cause". Is that how to think of it?

nathan-at-least avatar Oct 26 '23 18:10 nathan-at-least