Live data from Hacker News

Rust errors without dependencies

vincents.dev

61–70 of 91 posts

Re: Rust errors without dependencies

#61

Earlier quoted context omitted.

I doubt epage is suggesting that. And note that in that case, the thing distinguishing the cause is not `std::io::Error`, but `std::io::ErrorKind`. The latter is not the error type, but something that forms a part of the I/O error type. It's very rare that `pub enum Error { ... }` is something I'd put into the public API of a library. epage is absolutely correct that it is an extensibility hazard. But having a sub-or…

Why is it an extensibility hazard (assuming you mark the `pub enum Error` as non-exhaustive)? I mean I don't see the difference between having the non-exhaustive enum at the top level vs in a subordinate 'kind'.

Take the example at https://docs.rs/thiserror/latest/thiserror/

- Struct variant fields are public, limiting how you evolve the fields and types

- Struct variants need non_exhaustive

- It shows using `from` on an error. What happens if you want to include more context? Or change your impl which can change the source error type

None of this is syntactically unique to errors. This becomes people's first thought of what to do and libraries like thiserror make it easy and showcase it in their docs.

Re: Rust errors without dependencies

#62
post #61

Earlier quoted context omitted.

Why is it an extensibility hazard (assuming you mark the `pub enum Error` as non-exhaustive)? I mean I don't see the difference between having the non-exhaustive enum at the top level vs in a subordinate 'kind'.

Take the example at https://docs.rs/thiserror/latest/thiserror/ - Struct variant fields are public, limiting how you evolve the fields and types - Struct variants need non_exhaustive - It shows using `from` on an error. What happens if you want to include more context? Or change your impl which can change the source error type None of this is syntactically unique to errors. This becomes people's first thought of what…

Having private variants and fields would be useful, yeah. Std cheats a bit with its ErrorKind::Uncategorized unstable+hidden variant to have something unmatchable.

Re: Rust errors without dependencies

#63

Earlier quoted context omitted.

There's a semantics discussion about whether progressively adding context to errors (forming an error chain) counts as "error accumulation" or if error accumulation means collecting several errors that occurred and returning that to the caller. But at this point I think you understand what I meant and I understand your meaning. I do think that `std::error::Error` is mostly pointless. That's a value judgment, and reas…

> 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 po…

> 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` 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!

Re: Rust errors without dependencies

#64

Earlier quoted context omitted.

> I wrote about how to do error handling without libraries literally the day Rust 1.0 was published: https://burntsushi.net/rust-error-handling/ > > That blog did include a recommendation for `failure` at one point, and now `anyhow`, but it's more of a footnote. The blog shows how to do error handling without any dependencies at all. You didn't have to jump on the error library treadmill. (Although I will say that `a…

To clarify, I'm on libs-api. I've been on it since the beginning. Stabilizing `std::error::Error` was absolutely the right thing to do. There were oodles of things in Rust 1.0 that weren't stable yet that embedded use cases really wanted. There are still problems here (like I/O traits only being available in `std`). The zeitgeist of the time---and one that I'm glad we had---was to ship a stable foundation on which ot…

Let's continue discussion here: https://news.ycombinator.com/item?id=46416377

I think it's true that right up until 1.0, backtrace was a part of `trait Error`, then it was deprecated and removed, but there were ongoing discussions as late as 2021 about how the "point" of `std::backtrace::Backtrace` was to attach it to an `Error`. I have lots of links and references there.

As a user, that kind of meant that as long as I thought `trait Error` might grow backtraces someday, I should stay away from it in order to be friendly to embedded. And as long as it wasn't in core, that could still happen. I hope you can agree that it was far from clear what was going to happen until relatively recently.

Re: Rust errors without dependencies

#65

Earlier quoted context omitted.

To clarify, I'm on libs-api. I've been on it since the beginning. Stabilizing `std::error::Error` was absolutely the right thing to do. There were oodles of things in Rust 1.0 that weren't stable yet that embedded use cases really wanted. There are still problems here (like I/O traits only being available in `std`). The zeitgeist of the time---and one that I'm glad we had---was to ship a stable foundation on which ot…

> At some point, you've got to overcome the fear of getting it wrong and ship something. Also Editions mean we can go back and fix things "for the future" in some cases if that's worth the price. For example I believe it's worth the price for the built-in ranges to become IntoIterator, indeed I hoped that could happen in the 2024 edition. It was, I think we'd both agree, worth it to fix arrays in 2021. This is one of…

Yeah, the edition thing is cool, but there's also a cost to it, which is that over time you accumulate more and more support code in the compiler for really ancient editions.

I don't have a super good understanding of how it actually works in rustc. In C++ typically they would use name mangling tricks to try to keep new and old standard library symbols from clashing if they decided to break compatibility. Probably with rustc they are happier to leave it unspecified. I have a hard time building a mental model of what kinds of things would be totally impractical to change with an edition.

Re: Rust errors without dependencies

#66

Earlier quoted context omitted.

There's a semantics discussion about whether progressively adding context to errors (forming an error chain) counts as "error accumulation" or if error accumulation means collecting several errors that occurred and returning that to the caller. But at this point I think you understand what I meant and I understand your meaning. I do think that `std::error::Error` is mostly pointless. That's a value judgment, and reas…

> 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 po…

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.

Re: Rust errors without dependencies

#67
post #19

My controversial take on Rust errors is to use anyhow everywhere until you have an immediate demand for explicit Enums. YANGNI The pros for using anyhow are big: Easily stack errors together - eg file.open().context(path) -, errors are easily kept up to date, and easy to find where they occur. An enum error is pleasing when you're finished, but cumbersome in practice. It's niche situation that you need to write a fun…

If your error domain has only one useful category why not just create an error type with a useful message and be done with it. Why use anyhow at all? You are essentially saying the error domain is small so the work is tiny anyway. anyhow seems useful in the very top layers where you basically just want bubble the useful errors modeled elsewhere to a top layer that can appropriately handle them. I don't think a crate…

I'm trying to describe a rather large spectrum of situations, and how most of them are favorable (or not unfavorable) to anyhow.

I'm not saying the error domain is small per se.

Instead, one argument I'm making: what you're describing about errors bubbling up to the top layer, is what happens with the overwhelming majority of errors in my experience.

Whether the error space is large or small, just wait until you have an immediate need to treat one error different from the rest. It happens, it's just not common.

I didn't steelman the case for when to use enums, but in short: In a tiny error space or as valuable documentation.

(The doc value is undermined somewhat when projects use 1-big-error and functions that eg only returns 3 of the 6 possible errors)

I'm not advocating removing an Error enum. Just that writing one can and should be postponed, saving a lot of maintenance edits.

Re: Rust errors without dependencies

#68
post #19

My controversial take on Rust errors is to use anyhow everywhere until you have an immediate demand for explicit Enums. YANGNI The pros for using anyhow are big: Easily stack errors together - eg file.open().context(path) -, errors are easily kept up to date, and easy to find where they occur. An enum error is pleasing when you're finished, but cumbersome in practice. It's niche situation that you need to write a fun…

If your error domain has only one useful category why not just create an error type with a useful message and be done with it. Why use anyhow at all? You are essentially saying the error domain is small so the work is tiny anyway. anyhow seems useful in the very top layers where you basically just want bubble the useful errors modeled elsewhere to a top layer that can appropriately handle them. I don't think a crate…

>I don't think a crate should abdicate from modeling the error domain any more than they should abdicate from modeling the other types.

Yes, it's just harder. We usually have a pretty good idea what callers want from the happy path, but the range of things that callers may or may not want to do in case of an error is very broad.

Re: Rust errors without dependencies

#69

The fact that you either need a third party dependency or a large amount of boilerplate just to get decent error reporting, points to an issue in the language or std library design. I've started also dropping `thiserror` when building libraries, as I don't want upstream users of my libraries to incur this additional dependency, but it's a pain.

> points to an issue in the language or std library design.

Rust has a too-small stdlib because they want to avoid a calcified stdlib like C++ and Python, which both have too-big stdlibs.

This is a law of nature, your stdlib can either be too small or too big. It cannot be the right size. At least it isn't C.

Re: Rust errors without dependencies

#70
post #6
post #3

> In the recent Cloudlfare outage Cloudlflare's proxy service went down directly due to an unwrap when reading a config file. Me and many other developers jumped the shark, calling out Cloudflare on their best practices. In Cloudflare's defense they treated this file as trusted input and never expected it to be malformed. Due to circumstances the file became invalid causing the programs assumption's to break. "Truste…

And it was treated as potentially malformed and hence the panic. That's what panic is for! When invariants are not upheld at runtime, in Cloudflare's case an abnormal amount of entries IIRC. I mean, if the error was handled what would you have done if not crashing the service with an error message? I think the post's point is that you don't panic if someone submits a malformed PDF (you just reject their request) but…

Continue with the previous config. If the config-loading function was fallible, and that bubbled up somewhere, someone might say, "Oh this can fail, we should do blue-green for configs the same as we do for exes." With it being infallible and a panic, that gets hidden.

If I was designing a language to surpass Rust, I'd make panics opt-in. I think Rust has a team looking into no-panic but it's a funny loophole for a language that wanted to eliminate crashes.

Post reply on HN