For app code that is supposed to fail, you need full custom error setup.
For example in a a http server you want to cleanly convert error to http response and a record on server side for viewing later.
imo zig implements errors in language pefectly
Meanwhile Rust treats all possible error types the same way as long as they're wrapped in Result.
I didn't know that the Zig folks had embraced the Go philosophy of “the language designer knows better than you” for trivial stuff like that.
That's the kind of behavior that lead people to build their own version of things, incompatible with the whole ecosystem, just to be free from the petty arbitrary limitation.
The thing is, I don't disagree with the fundamental arguments that led to these decisions, I just think it goes a step too far and they're trying too hard. I understand they aim to reduce footguns in C with that language but that doesn't mean they have to prevent someone from wearing the wrong shoe for the occasion.
Did Zig really do that? Because even go didn't, it's just the default of gofmt and you can't override it, but not a hard error.
> Same with unused variables which are a compile-time error.
Which is a pain in the ass for anyone attempting to refactor or iterate on a go codebase. A warning is just the better option for this kind of things. That's the entire purpose of the concept of warning.
> In order to simplify everything, tabs are not allowed. Spaces are necessary; we can't ban spaces.
They seem to be open to the alternative but not to a solution that isn't forced on people:
> Maybe someday, we'll switch to tabs for indentation, spaces for alignment and make it a compile error if they are incorrectly mixed
Zig already has a builtin code formatter that automatically changes formatting to their preference, but that's not enough.
> That's the entire purpose of the concept of warning
Yes, but as far as I know the Zig compiler cannot emit warnings, only errors. Because warnings are potential footguns too and we can't have that.
Thanks nonetheless.
Okay they're kind of on to something here, because in my experience with C, warnings just get ignored. And you only get warning if you're doing something really suspicious in the first place.
Every serious C project I've worked on has used -Werror.
-Werror is a good default for CI, but not for local development.
And I just don't get why the compiler has to throw an error about a formatting preference when the builtin formatter already changes it accordingly. Any serious project can just mandate use of the formatter to keep things consistent and be done with it. Instead the github issues have people debate tabs vs spaces because the compiler took a side in order to end the "bikeshed of all bikesheds", ironically contributing to it.
Saying this must be done is awfully strong here. There are other tools at your disposal, like dyn Error and the anyhow crate.
Your options effectively become either writing that enum, or using a macro that reduces to that enum. Unless, of course, you're willing to have the function signature say "eh, this function just breaks sometimes. Could be anything really."
This is arguably worse than crashing with a stack trace (at least i can see a call path) or go's typical chain of human annotated error chains.
I agree it's an issue, and I hope the community centers around better practices.
The issue is figuring out when to not do that and wrap a low level error with a higher level error without losing the critical information and making things to generic.
In general, if you're making serious software, you shouldn't assume source knowledge at all in user facing messaging.
We all have moments when we're developing software and haven't fully developed the domain. Not requiring exhaustive error handling during this process is a massive help. Obviously, fully enumerated errors are good, and catering diagnostics to the users is even better. But there's more to software than shipping perfect code.
Nobody's saying perfection is 100% required, but there are ideals and we should aim for them and design with principle. One of those principals should be quality error messages.
Hell, the rust compiler itself is a great case study for how improving error messages for users massively helps with adoption and quality of life. It's pretty rare that you hit an internal compiler error (ICE) too which is nice.
But yes, this does require that people care about diagnostics to begin with. I don't think that's worth forcing; most of us develop this instinct through frustration at dealing with the fallout of someone not caring.
(And FWIW I find the combination of explicitly-structured Results that are manually passed up the stack with RAII to be a very, very sweet spot for development velocity, and also easy to teach. I very much prefer this to both exceptions and go's approach.)
I agree about needing to have care about it, but I was fighting the claim that the poor error handling is an issue with the language.
I wish there were a better way to enforce that you can’t use the early return syntax without adding context to the error in some structured way (so that the result is something resembling a stack trace.) Maybe a particular WrapsError trait that the compiler knows about, which constructs your error with the wrapped error and callsite information, so that you can annotate it properly, and is automatically used for `?` early-returns if the return type supports it.
At some point it does feel like we’re just reimplementing exceptions poorly though.
> or go's typical chain of human annotated error chains
It’s interesting you say this, because go has basically the exact same analogous problem: People can just do `if err != nil { return nil, err }`, and it results in the same problem. And the last time I did much go programming I remember having a very similar complaint to yours about Rust. Maybe more go programmers have begun to use annotations on errors since then? There’s nothing really different about rust here, anyhow/etc make it just as easy to annotate your errors.
1. create `with_err` function: some_mayerr_func().with_err_log()?
2. add 'newbie beginner mode(default)' / 'expert mode (optimized)' for rust:
- current Rust default is a bit too focused on code-optimization, too much so that it defaults to removing backtrace
- default should be 'show every backtrace even for handled errors (in my code, not the lib code -- bonus points for "is handled" checkbox)'
This doesn't work when the error is already MyError so you want to make sure that `context` contains everything you might want to know about the call stack. Eg if you're writing a web server application you might have an error at the network layer, the HTTP layer, the application layer or the DB layer. So your context can have four Option fields and each layer can populate its field with whatever context makes sense for it.
Your application gets a new context and you add another field? There must be a better way to do this.
FWIW, you may be able to see the stack traces by enabling them in an environment variable (export RUST_BACKTRACE=1). By default, errors are dumped without context, but enabling back traces may show stack traces as well as detailed human-written context (if such crates are in use).
In Go, the error handling is forced to be so verbose that adding a little bit of context doesn't significantly increase code size/effort, so programmers tend to actually add that context. Also, there is a very simple function for adding context to errors in the stdlib itself, fmt.Errorf.
In contrast, the difference in Rust between just returning an error (a ? at the end of the expression that can return one) vs adding context is much higher. Especially so since the standard library doesn't offer any kind of error context, so you either have to roll your own or use some error handling crate (apparently anyhow is very popular).
So, while at the language level there is little difference, the ecosystem and "incentives" are quite different.
I don't disagree with what you've said, but I think this is likely to be read too strongly.
Adding a map_err before the ? is only a big step up in verbosity because the ? alone is so very terse.
Feels like if you use them well they're a great tool, but sadly a lot of devs just don't use the available tools very well.
The ecosystem should probably enable backtraces by default though, for all GUI apps at a minimum, the overhead it adds is almost always worth it. It's not good practice to use "lazy" error handling in performance-oriented code anyways, it will generate tons of heap allocations and uses dynamic-dispatch.
falliable_func().context("failed to frob the peanut")?
Combine that with the `thiserror` crate for implementing errors within a library context. `thiserror` makes it easy to implement structured errors which embed other errors, and plays well with `anyhow`. match operator.propose(py).with_context(|| {
anyhow!(
"Operator {} failed while generating a proposal",
operator.repr(py).unwrap()
)
})? {
Which is a combination of `rustfmt` giving up on long lines and also not
formatting macros as well as functionshttps://github.com/rust-lang/rust/issues/141854
> I'll also note that this was quite annoying to debug since the error message in its entirety was `operation not supported on this platform`, you can't use RUST_BACKTRACE, rustfmt doesn't have extensive logging, and I don't fancy setting up WASM debugging. I resorted to tedious printf debugging. (Side rant: for all the emphasis Rust puts on compiler error messages its runtime error messages are usually quite terrible!)
Even with working debugging it's hard to find the error since you can't set a breakpoint on `Err()` like you can with `throw`.
`anyhow` is a very cool library but in the end it just felt a little ick to use, like I was being lazy or something
In the abstract, we are basically treating the error as a “separate thing” from the context in which it occurred. Both are of course strongly related, however the way some of these error libraries try to squash the concepts together can be quite opinionated and doesn’t always facilitate smooth interop.
Not saying this is the only way to deal with errors, but it’s just one way of thinking of the problem that I’ve had pretty good success with.
Reason: thiserror still requires std which greatly hinder its use for no_std context
suddenlybananas•1d ago