Comment by lionkor

21 hours ago

> Rust lacks a uniform error type

Rust has practically one error, it's the Error trait. The things you've listed are some common ways to use it, but you're entirely fine with just Box<dyn Error> (which is basically what anyhow::Error is) and similar.

Having many semantic options for error usage is functionally the same as having many error types, except worse.

  • They all convert seamlessly, and the enums make the branches explicit. Don't even need to check the documentation to find which errors supposedly exists like in Go with its errors.Is, errors.As, wrapping and what not.

    An easy rule before you make a knowledge based choice is Thiserror for libraries, helping you create the standard library error types and Anyhow for applications, easy strings you bubble up.

    Or just go with anyhow until you find a need for something else.

    https://crates.io/crates/anyhow

    https://crates.io/crates/thiserror

    • I’ve repeatedly tried using Rust and the error handling has tripped me up every time and has been ~90% of the reason for moving a project back to another language. I’m sure I’m just holding it wrong, but what I run into usually goes something like this (mind you, I have read the Rust book):

      * Someone tells me to use enums for errors, in a comment like yours

      * I try writing the enums by hand, implementing the error trait

      * I realize that in order to use the ? operator I need to implement From on my errors (I’ve read so many comments about how awfully verbose Go errors are, so I assume I’m supposed to use ? in Rust). There are also some other traits IIRC but I’ve forgotten them.

      * I realize that this is pretty tedious, manual work, so someone points me to thiserr or similar

      * Now I’m debugging macro expansion errors and spending approximately the same amount of time

      * I ask around and someone tells me not to bother with thiserr and to just write the boilerplate myself or else to use anyhow or boxed errors everywhere

      * I try using boxed errors everywhere, which works, but now I have all of these allocations which feels like I’m doing something that will bite me later. Oh well, but now I need to annotate my errors so I can figure out what is actually happening. I guess I should use anyhow for this?

      * Anyhow mostly works but this is approximately as verbose as the Go error handling that I’m told is Very Bad, and when I ask for code review most Rust people are telling me not to use anyhow because errors should be enums, at least in the API surface

      I’m sure I’m doing it wrong, but as with many things in Rust, the Right Way is so rarely clear and every other Rust person gives different advice about how to solve my problem and the only thing they seem to agree on is that Rust has an easy solution and that I’m following the wrong advice. (Similarly when I had lifetime problems and half the community told me to just use clone and Rc everywhere until I had performance problems, so instead I just had different static analysis problems).

      I don’t love Go’s error handling. It feels like there has to be something better than its runtime-typing. But it largely gets out of the way—creating an error is just implementing the Error method, and if you need a concrete type you use Is/As/AsType. Wrapping is fmt.Errorf. All of this is built into the stdlib and used pretty ubiquitously across the ecosystem—I don’t run into “this dependency uses a different error framework”. Error handling is marginally more verbose than with Rust if you are actually attaching context in both, and neither solves the problem of which call frame attaches the context about specific function parameters (e.g., which level of error context specifies that the function was called with path “/foo/bar.baz”). It’s terrible, but it works—feels like the least bad thing until the Rust community can arrive at some consensus and document it in The Book. Or maybe I just need to try again in the LLM era?

      11 replies →

Surely you need an alternative to Box<dyn Error> for reporting memory allocation failures?!

  • Anything other than panic/abort on allocation failure is outside the scope of the vast majority of programs, including anything using the standard library in Rust. I wouldn't worry about Box<dyn Error>.

  • A &(dyn Error + 'static) should be fine for that; you don't need any allocated/variable sized data in a memory allocation failure.

  • You're already writing Rust in a very different style if you're writing the type of code that gracefully handles allocation failure. It's to Rust's immense credit that this type of coding is actually fairly well-supported (unlike in Go), but you're already a bit off the beaten path for stuff like error handling.

  • Not sure what your problem is?

    If you need to handle an allocation error in the error path, then the error reporting path must abort, which means that the allocation error must be bubbled up.

    There is no real solution to an allocation error inside the error path. Even if you preallocate an arena for errors, the error might be large enough that it won't fit inside the arena.

    Hence the best thing you can do from that point onwards is to have an error enum with an AllocError variant that doesn't allocate. Said error won't contain any information beyond line numbers of the allocation error since you just don't have the space for it.

    In the end you will basically end up with panic free code, but the error still bubbles up like regular unwinding.

    So yeah you can do it, and I will do it in the future, but I personally think that the people who think this is some huge deal breaker don't understand the problem in the first place.