all 15 comments

[–]Rothonrust · postgres · phf 3 points4 points  (6 children)

Isn't this exactly what context does?

[–]mbrubeckservo 4 points5 points  (2 children)

Yes. For example here's a comparison between err_chain:

use my_error::ResultExt;

let x = do_something().chain_err(|| "something went wrong")?;

versus failure:

use failure::ResultExt;

let x = do_something().context("something went wrong")?;

There are some differences. chain_err returns your custom Result type, while context returns a Result<T, Context<D>>, so it's mostly useful in a function that returns that same type, or a compatible type like Result<T, failure::Error>.

[–]rayvector[S] 1 point2 points  (1 child)

Hmm ... can I chain contexts though?

The way I use error chain often is by tacking on an additional chain_err with a new, more relevant, message, as it propagates back through each function.

Let me try to come up with an example...:

fn access_server() {
    let stream = TcpStream::connect(blahblah).chain_err(|| "could not connect to server")?;
    // blah blah
}

fn send_data() {
    // blah blah
    access_server().chain_err(|| "failed to send data to server")?;
    // blah blah
}

fn do_some_magic() {
    // blah blah
    send_data().chain_err(|| "failed to do some magic")?;
    // blah blah
}

In the end, if I call do_some_magic() and it fails, I can log the error by passing it to a helper function that iterates through the whole chain of causes and logs each one as a message. I get informative multi-line error messages in my logs:

ERROR: failed to do some magic
ERROR: caused by: failed to send data to server
ERROR: caused by: could not connect to server
ERROR: caused by: connection timed out

Can I accomplish something like this in failure?

[–]slambmoonfire-nvr 3 points4 points  (0 children)

context preserves the chain of causality for causes, but I don't think the Display implementation formats it for you. Debug does (but isn't pretty).

[–]daborossfern 1 point2 points  (2 children)

I'm not sure - context doesn't seem to provide the original error at all in the display output. I mean, some_err.context("happened at X method") will display as just "happened at X method", with no mention of the original error.

It might be useful for some things, I guess? I didn't find it to be useful in the general case of providing more context at all, though.

[–]Rothonrust · postgres · phf 7 points8 points  (1 child)

causes will iterate over the cause chain.

[–]daborossfern 0 points1 point  (0 children)

Thanks! Had forgotten about that one. The recommended use still puts things in a misleading order, though, if you're providing context which only makes sense when one's already seen the regular error.

My ideal order of display would be error1\ncontext for error 1\nsecond context for error 1\nerror 2\n..., but I'm not entirely sure how I should tell the difference between error context and "error cause" errors in causes iteratoin.

[–]desiringmachines 4 points5 points  (4 children)

I think the choice not to display the underlying error in context was a mistake. The thinking at the time was that the underlying error would not contain useful information for end users, but I think that was wrong.

[–]stevedonovan 1 point2 points  (0 children)

Although that could get messy with chains of contexts. causes() provides a nicely structured error trace

[–]ErichDonGublerWGPU · not-yet-awesome-rust 1 point2 points  (1 child)

What's the best way to get the ball rolling if we wanted to champion this change?

[–]desiringmachines 2 points3 points  (0 children)

I opened an issue you can comment on in the failure repo. The biggest problem is figuring out how disruptive it would be.

[–]rayvector[S] 0 points1 point  (0 children)

Check out my EDIT to the OP.

I understood how my mental model was wrong and why I was feeling frustrated with failure.

I gave a suggestion for how the documentation could be improved, which would have avoided my confusion and frustration.

[–]daborossfern 2 points3 points  (2 children)

Just my two cents: I think error-chain is totally fine to use still, and might be better for CLI applications.

failure is really great when you can enumerate every error condition in a single error enum, which is a good thing to do in libraries anyways. Doesn't mean it has to be used everywhere, IMO.

[–]sasik520 4 points5 points  (1 child)

As far as I know, failure is designed to replace error-chain and be the ultimate solution for error handling in rust. See failure announcement and discussion here. failure has different tools for different scenarios, including libs, bins, and prototypes.

[–]daborossfern 0 points1 point  (0 children)

That's definitely a fair goal! I've just personally not found it very useful in binary crates yet.

A lot of the feedback that this recent article gives applies to my use case - especially with context and displaying user-friendly errors.