7 ms·
Don't Return Err in Go
- jtohasdfasd 2y agoWhat I see wrong with Go's error handling is that the error type is an open set which welcomes you with a Pandora's box full of infinite possible error values. This is much too burdensome for me to cognitively bear so I prefer a closed set model as seen in Zig, Rust, or any ML. I don't recall if Swift has exhaustive error handling.
- the_gipsy 2y agoFor small codebases it might work to just grep and hope to find unique error messages. But it feels very brittle. Go should have an option to turn on stacktraces for all errors. We wrap errors with stacktraces at library boundaries so that we can `return err` in most cases, unless we really want to add some information. But mostly we use sentry to push log messages with better data into breadcrumbs leading to the errors.
- SebastianKra 2y agoAs a TS dev, I found that this type of checking will blow up a codebase to the point where it becomes significantly harder to read. Worse, I've seen a good developer spend way too much thought and code on correctly reporting cases that realistically won't even ever happen. Since TS has stack traces, I find it much more practical to handle based on broader categories: Errors for users need some system for displaying messages, icons etc.. Errors for admins need similar handling, but it makes little sense to spend this much effort on pretty error handling when the error is for a developer.
- theamk 2y ago> Since TS has stack traces That's the key here - I would not need anything like this in Python either, because it has stack traces. But Go designers decided to really drop the ball here - there are no stack traces on "err" values, so one has to implement them by hand as article demonstrates. Which is a pity. Go has a lot of great things going for it, but the error handling just makes everything unnecessary verbose.
- feisuzhu 2y agoIs this poor man's stacktrace?
- rand_r 2y agoIf done comprehensively, this strategy has the potential to be much better than a traditional stacktrace because you can embed local variables within each additional error context string. The main problem with traditional stacktraces is that, while you get line numbers, local variables at every stack frame are not preserved. So unless the error is obvious like FileNotFound, you’re left guessing as to how the error happened. While still being hella verbose and probably too much effort for most code, the upfront investment would obviate the need for adding debug print statements or dropping into a breakpoint to inspect the code after an exception. That said, the post does feel like copium for the ridiculous verbosity in Go and re-inventing chained exceptions.
- mst 2y agoIt's more effort to code than a stack trace but it does tend to result in clearer errors.
- TheJCDenton 2y agoIf you have any architectural complexity in your program, good error handling often come with custom errors. Unique error strings just don't cut it, you need a better structured type than a string to apply any kind of logic.
- jozvolskyef 2y agoYour thesis is contradicted by almost every open source distributed system written in Go, including Kubernetes.
- samber 2y agoI built a drop-in replacement to `error` recently. Instead of manipulating strings, it adds context, stacktrace... in a structured way. https://github.com/samber/oops https://github.com/samber/oops
- doubloon 2y agoWhy do it feel like everyone is copying rust
- lnxg33k1 2y agoI feel like this is more of a pure oop suggestion, that rust, even as a non oop language, implemented quite well
- _lvbh 2y agoBecause Rust got it right. Zig is THE language I’m looking forward to growing which adopts a lot of the best parts of Rust while keeping things simple like Go.
- papichulo2023 2y agoIf only would have its amazing std :(
- throwaway2037 2y agoCan you provide some concrete examples of where the Rust standard lib is superior to the Zig standard lib? I'm talking about the library, not the language.
- toast0 2y agoI use Rust for work, and I mostly dislike it, but Result and ? are pretty nice (when the type system doesn't get in the way)
- dlivingston 2y agoWhat about Rust do you dislike?
- toast0 2y agoI posted a rant here... https://news.ycombinator.com/item?id=39121489 https://news.ycombinator.com/item?id=39121489 But I'll add that I'm happier in languages that are less structured with types and things. Perl, PHP, Erlang. Where I can just write my code and see if it works, and add checks later, if need be. Erlang's model of 'let it crash' is perfect for me, make the happy path with error checking, but only add error handling if you need to; with hotloading in production so there's little disruption. Faffing about with casting u64 to and from usize when everyone knows I'm only ever running on 64-bit is madness. C feels easier to write than Rust, but then that's because C doesn't care if you tell it to do something stupid, it'll just do it. Also, I don't generally use an IDE, because I'm happy with an editor in one shell and another to compile and run. I do use an IDE when making terrible Android apps and terrible Arduino kinds of things; they're useful, but I can't focus in an IDE the way I can in a terminal text editor, and I've got 25 years of experience in the text editor, so I'm not planning to change.
- porjo 2y agotl;dr return wrapped error instead
- secretlyadog 2y agoStill not seeing how this saves you from having to check if err != nil everywhere. Though I suppose if this were another language it would be akin to a try catch around every line of code. Which in a way is great, but pretty labor intensive.
- the_gipsy 2y agohttps://borgo-lang.github.io/#error-handling https://borgo-lang.github.io/#error-handling
- bryancoxwell 2y agoIt doesn’t save you from checking errors, and it’s not attempting to. It’s just making returned errors more useful.
- LorenzoGood 2y agoError wrapping is by far my favorite go language feature.
- euroderf 2y agoA stack trace is bare bones. A skeleton. Instead with %w you pass an error upward, wrapping it at each func with another text annotation, and at the top it emerges a fully-formed story. Or something like that. But anyways yeah error wrapping works great.
- steve_adams_86 2y agoThis is good advice. I had a hard time with this aspect of go until wrapping clicked. I think (as mentioned in the post) creating custom errors can be extremely useful as well, in moderation. Go error handling is really not that bad if you embrace it and use it well. Like many things in go, I think a lot of us unwittingly hope it will behave in familiar ways because so much of the language seems simple or intuitive. Unfortunately, without a good grasp on it, many parts of go will appear to cooperate with your hopes until it doesn’t. I think a great example of this is the type system. There is a correct way to use it, but it’ll let you misuse it quite extensively before things start to implode. Errors are a bit like this. You can return raw errors for a long time and it’ll seem fine until suddenly, debugging and tracing issues is incredibly inconvenient and unintuitive. I found the book “Learning Go” really helpful in breaking these bad habits and helping me get acquainted with how things actually work under the hood.
- mst 2y agoThe Jon Bodner one? There seems to be a few.
- steve_adams_86 2y agoOh, whoops. That’s the right one. I should have specified! I found it a perfect level to get a competent programmer familiar with Go without oversimplifying or omitting too much. I came away from it significantly more productive and confident with the language.
- _lvbh 2y agoGolang error handling is terrible by default. The lack of sum types also make it hard to know what errors a function might return and libraries rarely wrap their errors. There also isn’t any stack trace by default which makes it hard to find the original source of the error. Check out https://github.com/rotisserie/eris https://github.com/rotisserie/eris. It really is a lifesaver
- udev4096 2y agoGreat advice!
- vsnf 2y agoGiven how pointedly opinionated Go is about how codebases should look like (unused variables are a compiler error!?), I'm somewhat surprised that `if err != nil return err` isn't some kind of built in warning. I do think Go as a language encourages this bad behavior.
- the_gipsy 2y agoGo is not really opinionated about unused variables, it's opinionated about not having compiler warnings, only errors. Which was a mistake, given that external linting tools had to pick that up.
- bartonfink 2y agoGo is absolutely opinionated about unused variables, and its opinion is "that's a compiler error." It's part of what makes Go special. You should try writing some sometime.
- the_gipsy 2y ago"Unused vars are warnings" is not a possible opinion for go, period. It's also not a possible opinion for go tooling like 3rd party linters. Edit: my point is that it's not a design choice but a side effect of choosing "we don't have compiler warnings".
- d-z-m 2y ago> "Unused vars are warnings" is not a possible opinion for go, period. It's also not a possible opinion for go tooling like 3rd party linters. Right, but that doesn't mean that Go doesn't have an opinion about unused variables. Go's opinion is that they are compiler errors. > my point is that it's not a design choice but a side effect of choosing "we don't have compiler warnings". What makes you think that? I don't think "we don't have compiler warnings" was behind the choice to make unused variables a compiler error. It seems like a deliberate design decision on the part of the Go team.
- JodieBenitez 2y ago> Proponents of exceptions may say, “this is so much manual writing labor, exception stack trace would automate that!” Yes indeed. > by writing custom messages, we can provide more details useful for debugging (for example, the os.ReadFile helped us with a filename); Custom exceptions anyone ? > by not relying on code line numbers, the messages are more long-lived, and can actually be understood and reasoned about in isolation, without access to the source code. Doubt... I quite like Go (some aspects of it anyway) and it's proven both useful and successful, but some people will go great length to find virtues in its weaknesses.
- the_gipsy 2y agoYou don't need the horrible mess of exceptions to get stacktraces on errors. You can tack on stacktraces on go's errors manually, but it's somewhat limited. Rust has some env var to enable stacktraces IIRC, which is a better solution.
- throwaway2037 2y agoThis is a great post. I also respect Go. However, I don't understand the allergy to exceptions. Even the part about them adding extra debug details to the message. Hmm, looks just like a chained exception from Java/Python. Oh wait, without the helpful exception stack trace. It reminds me of the multi-year arguments about generics in Go. Obviously, they were needed, yet 100s(?) of blog posts were written about why we didn't need them. They have aged like fresh milk -- not well. See the same discussion about the keyword "final" in C++ to seal a class. For years, C++ was allergic to ideas from any other language that came after. Things are different now.
- mst 2y agoI think the reality re generics was that the authors of Go wanted to see what people did instead to understand the patterns of usage so when they -did- add generics they could provide the simplest thing required. Which is probably the only real way to figure out what 'simplest' is, I deliberately spent a couple of years writing OO perl code raw (i.e. the low level bless() based stuff that you normally only use for optimisations) to figure out which features of p3rl.org/Moose I couldn't live without - and came out the other side with a solid spec for p3rl.org/Moo which has since proven pretty much spot on as "what almost everybody needs almost all of the time." Using their entire userbase as guinea pigs rather than only themselves is only going to work better for that. (I'm not necessarily endorsing the choices, but I can see why they were made and why they were effective)