I remember people being so excited to use error values instead of exceptions in mainstream languages. But this libraries plus error values combo looks even more complex to me.
That is akin to saying using a GC is giving up on safe memory allocation, and the borrow checker is the only solution to a complex problem. Exceptions are handled gracefully in real world projects as well, otherwise Python, Java etc would have died a long time ago.
I'm not sure how this idea makes coherent sense. Best practices are going to be determined by users, so even in a language where there's a BDFL or some ludicrous committee flying to Hawaii to discuss everything in person a central "Best practice" document is not actually best practices it's just somebody's opinion but with a rubber stamp.
A central dogma approach just means you need more doublethink, the "best practices" still gradually change but now you're pretending they've always been this way even as they shift beneath your feet.
eg: if you want distributed systems, esp Java-style, https://www.martinfowler.com is a pretty handy reference. (similar for other areas of SWE).
It’s nice to have a single resource that documents a bunch of non-obvious but still-generally-accepted engineering practices relevant to a domain. It’s of course an opinion, but is reasonably uncontroversial. Or at least, you won’t go too wrong while you develop your own taste in said domain.
The entire software development industry has slept on error handling without exceptions and Rust developers are the first ones to actually start addressing the problems.
This problem should have been solved decades ago by C/C++ developers.
Except the java/python style with unchecked exceptions means an exception can happen at any time, there's no way to know in advance. This is what Rust is trying to avoid with the errors-as-values approach.
It has its drawbacks, yes, but I'd never go back to the wild-west YOLO approach of unchecked exceptions, personally.
A lot of things are broken in C++. Like coroutines, exceptions, parallel execution (std::execution) etc. That does not mean the core ideas are bad and we should stop using them in every language.
Really? Premature stabilization is a much more recognisable problem. This is after all quietly why there's so much Rust adoption. Safety is nice, but perf is $$$.
C++ had this unhealthy commitment to stabilization and it meant that their "Don't pay for what you don't use" doctrine has always had so many asterisks it's practically Zalgo text or like you're reading an advert for prescription medicine. You're paying for that stabilization, everywhere, all the time, and too bad if you didn't need it.
Unwinding stabilizations you regret is a lot more work even in Rust, consider the Range types, landing improved Range types is considerable work and then they'll need an Edition to change which types you get for the syntax sugar like 1..=10
Or an example which happened a long time ago, the associated constants. There are whole stdlib sub-packages in Rust which exist either primarily or entirely as a place for constants of a type, std::f32::MAX is just f32::MAX but the associated constant f32::MAX only finally stabilized in 1.43 so older code uses std::f32::MAX nevertheless if you learned to write std::f32::MAX you may only finally get deprecation messages years from now and they're stuck with keeping the alias forever of course because it's stable.
The following articles may also be of interest to the audience:
- The definitive guide to error handling in Rust [0]
- Error Handling for Large Rust Projects - Best Practice in GreptimeDB [1]
- Designing error types in Rust [2]
- Modular Errors in Rust [3]
[0]: https://www.howtocodeit.com/articles/the-definitive-guide-to... [1]: https://greptime.com/blogs/2024-05-07-error-rust [2]: https://mmapped.blog/posts/12-rust-error-handling [3]: https://sabrinajewson.org/blog/errors
I remember people being so excited to use error values instead of exceptions in mainstream languages. But this libraries plus error values combo looks even more complex to me.
Their code is more complex in some ways (for example, it’s verbose).
But in languages with exceptions, if you want to know how a function can fail, you have two options:
- Hope the documentation is correct (it isn’t)
- Read the body of the function and every function it calls
Reasonable people can disagree on the right approach here, but I know which I prefer.
Or catch the top level function and see every exception in your project? Tell me which language does not have a top level main function?
It is not more complex. It is a complex problem and raising exceptions is akin to just giving up (vs. properly handling and reporting the errors).
That is akin to saying using a GC is giving up on safe memory allocation, and the borrow checker is the only solution to a complex problem. Exceptions are handled gracefully in real world projects as well, otherwise Python, Java etc would have died a long time ago.
More on how best practices in Rust are not documented anywhere but in a bunch of unfindable blog posts. :(
And even then, there's no guarantee what you find is not outdated.
I'm not sure how this idea makes coherent sense. Best practices are going to be determined by users, so even in a language where there's a BDFL or some ludicrous committee flying to Hawaii to discuss everything in person a central "Best practice" document is not actually best practices it's just somebody's opinion but with a rubber stamp.
A central dogma approach just means you need more doublethink, the "best practices" still gradually change but now you're pretending they've always been this way even as they shift beneath your feet.
Think you need a healthy mix?
eg: if you want distributed systems, esp Java-style, https://www.martinfowler.com is a pretty handy reference. (similar for other areas of SWE).
It’s nice to have a single resource that documents a bunch of non-obvious but still-generally-accepted engineering practices relevant to a domain. It’s of course an opinion, but is reasonably uncontroversial. Or at least, you won’t go too wrong while you develop your own taste in said domain.
For Rust, there are a few blogs that lean more on fundamentals and language design (like https://without.boats/blog, https://smallcultfollowing.com/babysteps).
For misc Rust engineering, like the OP, I agree it’s quite scattered. I personally like to save good ones in my Feedbin as I encounter them
Rust has the Error type. Libraries are add-ons and not part of the language.
This has very little to do with Rust.
The entire software development industry has slept on error handling without exceptions and Rust developers are the first ones to actually start addressing the problems.
This problem should have been solved decades ago by C/C++ developers.
And we slowly circle back to what Rust should have done all along: exceptions with Java/Python-style causal chaining.
I wonder how loudly "the community" would scream at me if I published something that just used panics for all error reporting.
Except the java/python style with unchecked exceptions means an exception can happen at any time, there's no way to know in advance. This is what Rust is trying to avoid with the errors-as-values approach.
It has its drawbacks, yes, but I'd never go back to the wild-west YOLO approach of unchecked exceptions, personally.
I've been following Raymond Chen's recent series on writing a tracking C++ pointer class with interest.
Most of the articles start with "to fix the mistake we showed at the end of the last article", and end with "but now we've broken something else".
Needing to keep track of where exceptions can occur, so that you don't leave an operation half committed, sounds especially nasty: https://devblogs.microsoft.com/oldnewthing/20250827-00/?p=11...
A lot of things are broken in C++. Like coroutines, exceptions, parallel execution (std::execution) etc. That does not mean the core ideas are bad and we should stop using them in every language.
> The core issue is that Rust still hasn't stabilized backtrace propagation on errors.
I would actually strengthen this to: «The core issue with Rust is that Rust still hasn’t stabilized _»
Really? Premature stabilization is a much more recognisable problem. This is after all quietly why there's so much Rust adoption. Safety is nice, but perf is $$$.
C++ had this unhealthy commitment to stabilization and it meant that their "Don't pay for what you don't use" doctrine has always had so many asterisks it's practically Zalgo text or like you're reading an advert for prescription medicine. You're paying for that stabilization, everywhere, all the time, and too bad if you didn't need it.
Unwinding stabilizations you regret is a lot more work even in Rust, consider the Range types, landing improved Range types is considerable work and then they'll need an Edition to change which types you get for the syntax sugar like 1..=10
Or an example which happened a long time ago, the associated constants. There are whole stdlib sub-packages in Rust which exist either primarily or entirely as a place for constants of a type, std::f32::MAX is just f32::MAX but the associated constant f32::MAX only finally stabilized in 1.43 so older code uses std::f32::MAX nevertheless if you learned to write std::f32::MAX you may only finally get deprecation messages years from now and they're stuck with keeping the alias forever of course because it's stable.