logoalt Hacker News

burntsushiyesterday at 10:28 PM2 repliesview on HN

> I do think that `std::error::Error` is mostly pointless. That's a value judgment, and reasonable people can disagree.

I took it as a statement of fact. It is a factual matter of whether `std::error::Error` has a point to it or not. And it definitively does. I use the error chain in just about every CLI application I've built. It's not even mostly pointless. It's incredibly useful and it provides an interoperable point for libraries to agree upon a single error interface.

And one `Error::provide` is stable, it will be even more useful.

> I've tried to argue that, it can create bigger problems then it solves. It's a trait that only exists on platforms with `std`. That itself is pretty nasty and if you care at all about platforms that aren't like that, you're taking on a lot of build complexity. If you really need this why not just make your own `trait HasCause` which is like a subset of `std::error::Error` functionality, and simply doesn't require `std`?

The `Error` trait has been in `core` for about a year now. So you don't need any build complexity for it.

But you're also talking to someone who does take on the build complexity to make `std::error::Error` trait implementations only available on `std`. (Eventually this complexity will disappear once the MSRV of my library crates is new enough to cover `core::error::Error`.) But... there really isn't that much complexity to it? It's a very common pattern in the ecosystem and I think your words are dramatically overstating the work required here.

> 1) Why does it require `Display` and then not use it?

Because it defines the contract of what an "error" is. Part of that contract is that some kind of message can be generated. If it didn't require `Display`, then it would have to provide its own means for generating a message. It's not a matter of whether it's "used" or not. It's defining a _promise_.

> 2) Displaying it is very simple: `format!("{err}")`. If you want to format the error and it's chain of causes, actually using the `std::error::Error` functionality, the recommended way was to use yet another experimental `error_chain` library. When should we actually do that? When is that appropriate?

Who says it was "the recommended way"? I never recommended `error_chain`.

Writing the code to format the full chain is nearly trivial. I usually use `anyhow` to do that for me, but I've also written it myself when I'm not using `anyhow`.

> Now we have a place where there's two different ways to do the same thing (display an error). Additionally there is controversy and churn around it.

Yes, this is a problem. If something appears in the `Display` of your error type, then it shouldn't also appear in your `Error::source`. This is definitely a risk of getting this wrong if you're writing your error type out by hand. If you're using a library like `thiserror`, then it's much less likely.

> I understand that there's a bright shiny future that people hope it's headed for, where everything around `std::error::Error` is easy and obvious, and we have powerful flexible expressive ergonomic error handling. I was excited about that like 7 years ago, now I just kinda want to change the channel. I'm glad some people still find some benefit in the small improvements that have occurred over time... and I hope in 8 more years there's more to the story than where we are today.

I was very happy with error handling in Rust at 1.0 personally.

I think people got burned by the churn of the error library treadmill. But you didn't have to get on that treadmill. I think a lot of people did because they overstate the costs of write-once boiler plate and understate the costs of picking the wrong foundation for errors.


Replies

osiris88today at 3:16 AM

Let me be clear cause I think my initial post was harsher sounding than I intended.

I love working in rust. I love Result, and the ? sigil, etc. I love the enums and match, and how non_exhaustive works. I love all that.

I think that means I love rust error handling as well!

I just didn't love `std::error::Error`, it caused some pain. I think they should have just waited to stabilize until it was ready to go in core. If it wasn't there on day 1, rust error handling would have worked great! It's actually a pretty small and inessential part of the rust error handling story. I mean at this point I've hardly used it at all in 8 years using rust almost every day.

And all those churning crates, failure etc., like, that was just some people's opinions about what a fancier error framework might look like. And absolutely you're right we didn't need to get on that treadmill.

I wanted to support the OP's minimalist take though and complement it with my own though -- for a certain type of engineer that I have worked with, "use std::error::Error` looks like a "best practice" and that means that we aren't writing "good" or "idiomatic" rust if we don't use it. I do think it's a completely valid choice to eschew it. But it is somewhat harder to justify if that trait is in core now.

osiris88today at 1:26 AM

> I took it as a statement of fact. It is a factual matter of whether `std::error::Error` has a point to it or not. And it definitively does. I use the error chain in just about every CLI application I've built. It's not even mostly pointless. It's incredibly useful and it provides an interoperable point for libraries to agree upon a single error interface.

Okay, I'm willing to give you this one. I haven't encountered this view among coworkers before. The consensus view in my circles was, there is potentially a very small upside if your consumer is willing to do the error chain song and dance, but most people don't, and you have a risk of a lot of complexity for no_std builds, so it's better to avoid. But that may be a biased take and there may be lots of applications where the error chains are really great, and I just haven't encountered them.

> The `Error` trait has been in `core` for about a year now. So you don't need any build complexity for it.

I actually had no idea that it has been in `core for a year now!!

I am very happy, this means that the situation has actually improved dramatically and there's no major downside to using `std::error::Error`.

I'm going to re-evaluate my choices. I have a lot of code that systematically avoids `std::error::Error`, and I'm not sure it's worth it to change it all, but there's probably no good reason to avoid it if it's in core now.

---

I think you are mistaken, however, about backtraces never being a part of std::error::Error. There are RFC's from 2018 years ago that talk all about it:

https://rust-lang.github.io/rfcs/2504-fix-error.html

Here's a withoutboats PR in 2020: https://github.com/rust-lang/rust/pull/72981

https://github.com/rust-lang/rust/pull/72981#issuecomment-66...

It may just be a matter of perspective -- if you don't count rust pre 1.0, then yeah it "never" involved backtrace. But in my company, we were trying to use rust even at that time for no_std targets, and trying to figure out where the puck was headed on std::error::Error was complicated. All the backtrace stuff made us think, maybe this is just not meant for us and we should rip it out, and we did eventually, although not without a lot of internal arguments.

> It's incredibly useful and it provides an interoperable point for libraries to agree upon a single error interface. > > And one `Error::provide` is stable, it will be even more useful.

Now it's clear to you that the "point" of it is error chains, that was maybe not a consensus view of the libs team on the day of 1.0.

Even in 2021 we have comments like this (https://github.com/rust-lang/rust/pull/72981#issuecomment-76...):

> We discussed this at the recent Libs meeting and came to the conclusion that stabilizing Backtrace without Error::backtrace wouldn't be a useful direction, given that Backtrace is designed around being carried alongside Errors. So for now we can consider the stabilization blocked pending figuring out the last bits of what a pluggable (whether stably or not) backtrace would look like.

>

> We can move design discussion over to #77384

Because if the only purpose was error chains, it could have been in core on the day of rust 1.0, as it is today. I think what actually happened is `fn backtrace(&self) -> Option<Backtrace>` was removed shortly before 1.0, but there were some misgivings about that and some on the core team wanted to bring that back eventually. And that was the main reason that it could not move to core, because that would make it a breaking change to bring `fn backtrace` back. At least that's what I remember from PRs I followed at the time. (There may have been other reasons besides this though?)

So, hearing that it is now actually in core is great, that resolves uncertainty I've had for like 7 years. Thank you!

show 1 reply