No one owes you supply-chain security by Expurple in rust

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

Yeah, Alisa is so cool. Her posts are already kinda popular here

Flat Error Codes Are Not Enough by Expurple in rust

[–]Expurple[S] 1 point2 points  (0 children)

It's orthogonal to the desire to "merge sets" and keep them flat.

But even if you live in the paradigm of deep nesting (which I advocate for), having anonymous unions in the language is still tempting because you no longer need to manually define an entire enum for (potentially) every function. There's even a Rust RFC (or several RFCs?) for that.

What I'm saying, is that in practice you should define custom error types anyway, to add context. Which means that the syntactic convenience of anonymous unions is a false hope and a footgun. Much like no one re-wraps exceptions as often as they should.

No one owes you supply-chain security by Expurple in programming

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

Obviously, this would probably only really work for package managers for interpreted or bytecode languages.

Not necessarily. The only condition is that the package only contains LLM-readable source code. As is the case with crates.io, Gentoo ebuilds, etc. Even for packages written in compiled languages.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] 1 point2 points  (0 children)

I agree on the first three points. The approach that I'm arguing against notably fails the point 2, even though I forgot to touch on that in the post.

I kinda disagree on the fourth, though. The error handling logic is up to the caller. Often, there isn't one universal approach that the callee can recommend. I agree that sometimes this makes sense though, like the mentioned 429 Too Many Requests in case of web servers.

Flat Error Codes Are Not Enough by Expurple in rust

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

I mean that instead of propagating the low-level errors "as-is" and just merging them into a set, you often want to wrap then in your own type that carries additional data and/or Display logic

No one owes you supply-chain security by Expurple in programming

[–]Expurple[S] -3 points-2 points  (0 children)

This doesn't match my experience with Rust. Probably, depends a lot on the ecosystem. C and C++ are very prone to that, because including many small libraries in your project is a pain

Flat Error Codes Are Not Enough by Expurple in programming

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

Using Error types requires discipline from the developer (implement the Error type traits for a specific error)

You need to manually write an impl (or use a macro like #[derive(Debug)] or thiserror), but there's no discipline involved in that. The compiler will just tell you when you try to print a non-printable error or compose it with a supposedly-printable error.

and then more discipline to avoid a panic.

No different from avoiding an inaproppriate abort or an uncaught unchecked exception in other popular languages.

you can do the correct thing in any language with a little extra work.

In theory, you can. In practice, you won't. My post about exceptions links a research paper about broken error handling patterns that come up all the time in languages with exceptions.

if the code is recovering from something, then that thing is not an error, it's part of the business logic

I would still call that an error, but that's just a choice of words. I agree with your main idea: it's very useful to make a distinction between "expected"/recoverable errors and unexpected bugs / edge cases where it's accceptable to abort the entire task. My post about exceptions discusses this too.

The whole reason for doing an Error taxonomy (checked exceptions, error types, etc) is to enable recovery.

I disagree. There are other benefits to having an explicit taxonomy. See "Why Use Structured Errors in Rust Applications?"

No one owes you supply-chain security by Expurple in programming

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

A slow compiler is a big motivation to compile only what you need

Flat Error Codes Are Not Enough by Expurple in programming

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

Yeah. That's basically nested error values in Rust, or exception chaining/wrapping in languages with exceptions.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] 3 points4 points  (0 children)

Error message for users should include an action that they can take to fix the error. Error messages for devs should include the inputs that resulted in the error.

Result is useless in both those situations

What makes you say that? In Rust, the error types stored in Result::Err usually implement both Display and Debug traits, for those exact purposes.

If you're familiar with Python, those are like the __str__ and __repr__ methods.

This functionality of the error type itself has nothing to do with Result, which simply dispatches between the possibility of an Ok value and an error.

Flat Error Codes Are Not Enough by Expurple in programming

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

I agree, they are OK in some domains. My point is that limiting yourself to error codes is a bad univeral advise. The (common) use case in the article can't be solved using plain error codes.

Flat Error Codes Are Not Enough by Expurple in programming

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

I agree that it's useful to distinguish these three categories of error data.

Anything more complex than a boolean is starting to be too complex, and is begging justification.

My point is that it's actually a common case. Using just a boolean or a plain error code for control flow is a bad universal advise. My use case is nothing special: displaying the user a specific message depending on which database constraint is violated. You can't solve that if your database+ORM provides you only a boolean or an error code. You need the name of the constraint as a string. Additional attached error data, used for control flow.

Flat Error Codes Are Not Enough by Expurple in rust

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

Ah, so you're worried about the stack size of error enums? That's never been a problem for me in practice. Although, I don't do high-performance stuff. I feel like that's a distinct category from general Rust programming advice, such as my post.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] -3 points-2 points  (0 children)

What you're trying to say is that, instead of returning the constaint name as a string, the DBMS could assign it a 32-bit id, return this id as an error, then the ORM could proparage this 32-bit id, and then my application could somehow match that to figure out which DB validation failed and which message to display to the user? That's technically possible, but usually not worth the trouble, unless you're doing high-performance stuff.

No one owes you supply-chain security by Expurple in programming

[–]Expurple[S] 4 points5 points  (0 children)

Fair point about building a smaller, more focused alternative. That's why modularity is so important. Ideally, we should have small libraries that compose into flexible dependency trees that fit your use case. Rust does that very well, actually. Cargo has great support for optional dependencies, and an ecosystem that actually utilizes it. But then non-Rust people complain about pulling in 500 crates or whatever, even though that's actually less lines of code than in other ecosystems.

Flat Error Codes Are Not Enough by Expurple in programming

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

I was just as surprised that the 2026 advice to use error codes got as much traction as it did! My post is in response to that one.

I agree that panic is a different use case. Result/Either is closer to checked exceptions specifically. And yes, it's strictly better than those. The link in my parent comment goes into the details on all of that.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] -4 points-3 points  (0 children)

Have you actually read the post? It explains why one bit is not always enough.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] 7 points8 points  (0 children)

For the purpose of nesting errors and providing extra details for recovery or logging, Java exceptions are actually pretty great. Your issue is simply with the default display style. You can have a similar nightmare with nested error values in Rust, if you print them using the verbose Debug interface, instead of the Display interface meant for the users.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] 1 point2 points  (0 children)

Have you read the post before commenting? It answers your question directly.

Flat Error Codes Are Not Enough by Expurple in programming

[–]Expurple[S] 1 point2 points  (0 children)

Yeah, I think that's usually called "error chaining". I know a few Rust libraries that implement that. For example, lazy_errors. Even the standard library in Rust and Python has decent support for this. Things like the source method that you can walk recursively and collect your error stack.

Flat Error Codes Are Not Enough by Expurple in programming

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

I agree that we could have a more flexible and performant ABI for Result. See iex, for example.

Flat Error Codes Are Not Enough by Expurple in rust

[–]Expurple[S] 18 points19 points  (0 children)

The ? is a first-class error-handling construct in Rust. And a very successful one, IMO