(no title)
dvratil | 10 months ago
Then you top it on with `?` shortcut and the functional interface of Result and suddenly error handling becomes fun and easy to deal with, rather than just "return false" with a "TODO: figure out error handling".
dvratil | 10 months ago
Then you top it on with `?` shortcut and the functional interface of Result and suddenly error handling becomes fun and easy to deal with, rather than just "return false" with a "TODO: figure out error handling".
jeroenhd|10 months ago
SerenityOS is the first functional OS (as in "boots on actual hardware and has a GUI") I've seen that dares question the 1970s int main() using modern C++ constructs instead, and the API is simply a lot better.
I can imagine someone writing a better standard library for C++ that works a whole lot like Rust's standard library does. Begone with the archaic integer types, make use of the power your language offers!
If we're comparing C++ and Rust, I think the ease of use of enum classes/structs is probably a bigger difference. You can get pretty close, but Rust avoids a lot of boilerplate that makes them quite usable, especially when combined with the match keyword.
I think c++, the language, is ready for the modern world. However, c++, the community, seems to be struck at least 20 years in the past.
jchw|10 months ago
A long time ago, there was talk about a similar concept for C++ based on exception objects in a more "standard" way that could feasibly be added to the standard library, the expected<T> class. And... in C++23, std::expected does exist[1], and you don't need to use exception objects or anything awkward like that, it can work with arbitrary error types just like Result. Unfortunately, it's so horrifically late to the party that I'm not sure if C++23 will make it to critical adoption quickly enough for any major C++ library to actually adopt it, unless C++ has another massive resurgence like it did after C++11. That said, if you're writing C++ code and you want a "standard" mechanism like the Result type, it's probably the closest thing there will ever be.
[1]: https://en.cppreference.com/w/cpp/utility/expected
jll29|10 months ago
Good point. A language that gets updated by adding a lot of features is DIVERGING from a community that has mostly people that still use a lot of the C baggage in C++, and only a few folks that use a lot of template abstraction at the other end of the spectrum.
Since in larger systems, you will want to re-use a lot of code via open source libraries, one is inevitably stuck in not just one past, but several versions of older C++, depending on when the code to be re-used was written, what C++ standard was stable enough then, and whether or not the author adopted what part of it.
Not to speak of paradigm choice to be made (object oriented versus functional versus generic programmic w/ templates).
It's easier to have, like Rust offers it, a single way of doing things properly. (But what I miss in Rust is a single streamlined standard library - organized class library - like Java has had it from early days on, it instead feels like "a pile of crates").
moomin|10 months ago
d_tr|10 months ago
Rucadi|10 months ago
However it seems like C++ wants to only provide this kind of pattern via monadic operations.
zozbot234|10 months ago
This isn't really true since Rust has panics. It would be nice to have out-of-the-box support for a "no panics" subset of Rust, which would also make it easier to properly support linear (no auto-drop) types.
kelnos|10 months ago
Even then, though, I do see a need to catch panics in some situations: if I'm writing some sort of API or web service, and there's some inconsistency in a particular request (even if it's because of a bug I've written), I probably really would prefer only that request to abort, not for the entire process to be torn down, terminating any other in-flight requests that might be just fine.
But otherwise, you really should just not be catching panics at all.
bionhoward|10 months ago
You can configure your lints in your workspace-level Cargo.toml (the folder of crates)
“””
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
# arithmetic_side_effects = "deny"
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
“””
then in your crate Cargo.toml “””
[lints]
workspace = true
“””
Then you can’t even compile the code without proper error handling. Combine that with thiserror or anyhow with the backtrace feature and you can yeet errors with “?” operators or match on em, map_err, map_or_else, ignore them, etc
[1] https://rust-lang.github.io/rust-clippy/master/index.html#un...
codedokode|10 months ago
arijun|10 months ago
Also there is the no_panic crate, which uses macros to require the compiler to prove that a given function cannot panic.
alexeldeib|10 months ago
not sure what the latest is in the space, if I recall there are some subtleties
johnisgood|10 months ago
dvt|10 months ago
So, while this is an improvement over C++ (and that is not saying much at all), it's still implemented in a pretty clumsy way.
singingboyo|10 months ago
Doing error handling properly is hard, but it's a lot harder when error types lose information (integer/bool returns) or you can't really tell what errors you might get (exceptions, except for checked exceptions which have their own issues).
Sometimes error handling comes down to "tell the user", where all that info is not ideal. It's too verbose, and that's when you need anyhow.
In other cases where you need details, anyhow is terrible. Instead you want something like thiserror, or just roll your own error type. Then you keep a lot more information, which might allow for better handling. (HttpError or IoError - try a different server? ParseError - maybe a different parse format? etc.)
So I'm not sure it's that Result is clumsy, so much that there are a lot of ways to handle errors. So you have to pick a library to match your use case. That seems acceptable to me?
FWIW, errors not propagating via `?` is entirely a problem on the error type being propagated to. And `?` in closures does work, occasionally with some type annotating required.
ackfoobar|10 months ago
If you use `anyhow`, then all you know is that the function may `Err`, but you do not know how - this is no better than calling a function that may `throw` any kind of `Throwable`. Not saying it's bad, it is just not that much different from the error handling in Kotlin or C#.
maplant|10 months ago
skrtskrt|10 months ago
mdf|10 months ago
[1] https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
[2] https://doc.rust-lang.org/std/panic/fn.set_hook.html
[3] https://doc.rust-lang.org/nomicon/panic-handler.html
ekidd|10 months ago
fpoling|10 months ago
In any case I will take Rust Result over C++ mess at any time especially given that we have two C++, one with exception support and one without making code incompatible between two.
jandrewrogers|10 months ago
I use completely custom error handling stacks in C++ and they are quite slick these days, thanks to improvements in the language.
kccqzy|10 months ago
Back at Google, it was truly an error handling nirvana because they had StatusOr which makes sure that the error type is just Status, a standardized company-wide type that stills allows significant custom errors that map to standardized error categories.
jasonjmcghee|10 months ago
I like rust, but its not as clean in practice, as you describe
ryandv|10 months ago
Then implement the `From` trait to map errors from third-party libraries to your own custom Error space:
Now you can convert any result into a single type that you control by transforming the errors: There is a little boilerplate and mapping between error spaces that is required but I don't find it that onerous.unknown|10 months ago
[deleted]
unknown|10 months ago
[deleted]
Cloudef|10 months ago
koakuma-chan|10 months ago
loeg|10 months ago
That said, I'd prefer to be working in Rust. The C++ code we call into can just raise exceptions anywhere implicitly; there are a hell of a lot of things you can accidentally do wrong without warning; class/method syntax is excessively verbose, etc.
stodor89|10 months ago
0x1ceb00da|10 months ago
ryandrake|10 months ago
1: https://news.ycombinator.com/item?id=41543183
zaphar|10 months ago
It's obviously subjective in many ways. However, what I dislike the most is that try/except hides the error path from me when I'm reading code. Decades of trying to figure out why that stacktrace is happening in production suddenly has given me a strong dislike for that path being hidden from me when I'm writing my code.
skrtskrt|10 months ago
it can look just like a more-efficient `except` clauses with all the safety, clarity, and convenience that enums provide.
Here's an example:
* Implementing an error type with enums: https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/main/... * Which derives from a more general error type with even more helpful enums: https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/main/... * then some straightforward handling of the error: https://git.deuxfleurs.fr/Deuxfleurs/garage/src/branch/main/...
cmrdporcupine|10 months ago
https://github.com/abseil/abseil-cpp/blob/master/absl/status...
dabinat|10 months ago
I could of course create my own type for this, but then it won’t work with the ? operator.
atoav|10 months ago
I could imagine situations where an empty return value would constitute an Error, but in 99% of cases returning None would be better.
Result<Option> may feel clunky, but if I can give one recommendation when it comes to Rust, is that you should not value your own code-aesthetical feelings too much as it will lead to a lot of pain in many cases — work with the grain of the language not against it even if the result does not satisfy you. In this case I'd highly recommend just using Result<Option> and stop worrying about it.
You being able to compose/nest those base types and unwraping or matching them in different sections of your code is a strength not a weakness.
Arnavion|10 months ago
estebank|10 months ago
dicytea|10 months ago
This is what the Try[^1] trait is aiming to solve, but it's not stabilized yet.
[^1]: https://rust-lang.github.io/rfcs/3058-try-trait-v2.html
vjerancrnjak|10 months ago
Just need a function that allows lifting option to result.
0x457|9 months ago
divan|9 months ago
Nothing prevents people from doing their own way (error int codes, bool handling, Result types, etc, panic), but it's just an easiest way that handles well 99% of the error handling cases, so it sticks and gives a nice feeling of predictability of error handling patterns in Go codebases.
ttfkam|9 months ago
In Rust, you can't just skip error handling. You have to proactively do something generally unwise (and highly visible!) like call .unwrap() or you have to actually handle the error condition.
Go still relies on goodwill and a good night's sleep. The Rust compiler will guard against laziness and sleep deprivation, because ultimately programming languages are about people, not the computers.
flohofwoe|10 months ago
And those 'special' stdlib types wouldn't be half as useful without supporting language syntax, so why not go the full way and just implement everything in the language?
choeger|10 months ago
You might add syntactic sugar on top, but you don't want these kinds of things in your fundamental language definition.
scotty79|10 months ago
I really wish java used `?` as a shorthand to declare and propagate checked exceptions of called function.
fooker|10 months ago
Unless you specifically want the ‘?’ operator, you can get pretty close to this with some clever use of templates and operator overloading.
If universal function call syntax becomes standardized, this will look even more functional and elegant.
steveklabnik|10 months ago
chickenzzzzu|10 months ago
after all, if a library exposes too many functions to you, it isn't a good library.
what good is it for me to have a result type if i have to call 27 functions with 27 different result types just to rotate a cube?
bena|10 months ago
Why can't I return an integer on error? What's preventing me from writing Rust like C++?
tczMUFlmoNk|10 months ago
For instance, a common example of the "integer on error" pattern in other languages is `array.index_of(element)`, returning a non-negative index if found or a negative value if not found. In Rust, the return type of `Iterator::position` is instead `Option<usize>`. You can't accidentally forget to check whether it's present. You could still write your own `index_of(&self, element: &T) -> isize /* negative if not found */` if that's your preference.
https://doc.rust-lang.org/std/iter/trait.Iterator.html#metho...
bonzini|10 months ago
tomp|10 months ago
In my experience, a lot of the code is dedicated to "correctly transforming between different Result / Error types".
Much more verbose than exceptions, despite most of the time pretending they're just exceptions (i.e. the `?` operator).
Why not just implement exceptions instead?
(TBH I fully expect this comment to be downvoted, then Rust to implement exceptions in 10 years... Something similar happened when I suggested generics in Go.)
nomel|9 months ago
Being blind to the alternative, and mostly authoring lower level libraries, what's the benefit of not having exceptions? I understand how they're completely inappropriate for an OS, a realtime system, etc, but what about the rest? Or is that the problem: once you have the concept, you've polluted everything?
throw10920|9 months ago
ttfkam|9 months ago
nextaccountic|9 months ago
Exactly like try catch
steveklabnik|9 months ago
Counterpoint: there are many non-trivial programs written in Rust, and they use Result for error handling.
90s_dev|10 months ago
But I hear compiling is too slow.
Is it a serious problem in practice?
Seattle3503|10 months ago
Besides developer productivity it can be an issue when you need a critical fix to go out quickly and your pipelines take 60+ minutes.
juliangmp|10 months ago
Like with any bigger C++ project there's like 3 build tools, two different packaging systems and likely one or even multiple code generators.
conradludgate|10 months ago
Cargo also has good caching out of the box. While cargo is not the best build system, it's an easy to use good system, so you generally get good compile times for development when you edit just one file. This is along made heavy use of with docker workflows like cargo-chef.
throwaway76455|10 months ago
cmrdporcupine|10 months ago
Granted, there aren't any Rust projects that large yet, but I feel like compilation speeds are something that can be worked around with tooling (distributed build farms, etc.). C++'s lack of safety and a proclivity for "use after free" errors is harder to fix.
mynameisash|10 months ago
Is it a serious problem? I'd say 'no', but YMMV.
ttfkam|9 months ago
So if you're cool with C++ or Java compile times, Rust will generally be fine. If you're coming from Go, Rust compiles will fell positively glacial.
kelnos|10 months ago
zozbot234|10 months ago
tubs|10 months ago
epage|10 months ago
hoppp|10 months ago
craftkiller|10 months ago