4 ms·
I have been using Golang recently and have the exact opposite feeling about it, which is likely due to my limited exposure to it. Needing to constantly check if
by jasongill 2y ago
I have been using Golang recently and have the exact opposite feeling about it, which is likely due to my limited exposure to it. Needing to constantly check if a specific return value is not nil just seems so sloppy but I have yet to come across a "better" way as it appears to just be a Golang convention. Is there a good post about better ways to handle errors in Go?
- devjab 2y agoThis is the better way as far as I’m concerned. Go has a philosophy to be simple, and this is explicit error handling at its simplest. You deal with errors exactly where they occur. I think a lot of people simply prefer implicit error handling and long exception chains. There is nothing wrong with that but that’s not how Go does error handling. After a couple of decades in the industry I really prefer explicit error handling because while powerful implicit error handling is also easy to get wrong. Which means I have to go through ridiculous chains of exceptions to find some error someone introduced years ago, instead of being able of heading directly to it and immediately understanding what is going on. So I guess it’s a little contradictory that I prefer the Rust approach, but I think the compromise of added complexity is small enough to make up for the added safety and “manoeuvrability”. The flip-side is of course that Go’s simplicity is part of the reason it’s seeing a lot of adoption while most other “new” languages aren’t. (Depending on where you live).
- treflop 2y agoI prefer implicit error handling if exceptions are checked and defined as part of the contract, otherwise it’s definitely hard to trace. I know the biggest complaint with checked exceptions is that people tend to just use catch all exceptions, but that’s just sloppy work. If they’re a junior, that’s when you teach them. Otherwise, people who do sloppy work are just sloppy everywhere anyway.
- devjab 2y agoMy issue with a lot of the best practice principles in SWE is that they were written for a perfect world. Even the best developers are going to do sloppy work on a Thursday afternoon after a day of shitty meetings during a week of almost no sleep because their babies were crying. Then there are there times when people have to cut corners because the business demands it. A million little things like that, which eventually leads to code bases which people like Uncle Bob will tell you were made by people who “misunderstood” the principles. Simplicity works because it’s made for the real world. As I said I personally think Rust did it better, but if you asked me to come help a company fix a codebase I’d rather do it for a Go one than Rust.
- treflop 2y agoTruthfully, I disagree. I’ve worked at a few different companies and I could absolutely rank them in the quality of their staff. Been on teams where every individual was free to use their best judgement, we didn’t have a lot of documented processes, and… nothing ever went wrong. Everyone knew sloppy work would come back and bite, so they just didn’t ever do it. Deadlines were rarely a problem because everyone knew that you had to estimate in some extra time when presenting to stakeholders. And the team knew when to push back. On the other hand, I’ve been on teams where you felt compelled to define every process with excruciating detail and yet experienced people somehow screwed up regularly. We didn’t even have hard deadlines so there was no excuse. The difference between implicit and explicit error handling would have not mattered. At the end of the day, some of these teams got more done with far fewer failures.
- closeparen 2y agoAlmost all error-related code in Go is just doing by hand what an exception system would do automatically: stop the current function and propagate it to the caller. The error is not being "dealt with" in a meaningful way except at the outermost layers of the onion (i.e. HTTP/RPC middleware converting it to a wire protocol error response) which could have used a try/catch anyway.
- wruza 2y agoGo has a philosophy to be simple, and this is explicit error handling at its simplest. You deal with errors exactly where they occur. “if err != nil {return nil, err}” is the opposite of this philosophy. If you find yourself passing err up the call stack most of the times and most calls may return err, it’s still exception-driven code but without ergonomics. It’s not simplicity, it’s head butt deep in the sand.
- kaba0 2y agoThere is no other way - most error conditions can't be dealt with at the place where the error happened. A parseInt failing is meaningless at the local scope, the error handling depends on who is the caller.
- wruza 2y agoYes, that’s the point. The idea that you deal with errors locally doesn’t work. See also https://news.ycombinator.com/item?id=42038568 https://news.ycombinator.com/item?id=42038568
- tomjen3 2y agoGolang needs monads. If by default it had a monad that propagated errors up the stack, then it would be much cleaner to work with, and should you need another behaviour you could install a different monad.
- ncruces 2y agoThere is no better way. The general idea is that you should pay the cost of handling an error right there where you receive one, even if you're just going to return it. This reduces the incremental cost of actually doing something about it. If you're given an easy way out, a simpler way of just not handling errors, you either won't even consider handling them, or you'll actively avoid any and all error handling, for fear of polluting your happy path. I can't say I agree with the approach all the time, but I know I'm much more likely to consider the error flow doing it the Go way, even if I'm comstant annoyed by it.
- Buttons840 2y ago> If you're given an easy way out, a simpler way of just not handling errors Doesn't go offer the simplest way of all to "just not handle errors"? Just ignore them. It is an option. You can ignore them on purpose, you can ignore them by mistake, but you can always simply ignore them.
- ncruces 2y agoYeah, sure. But ignoring by mistake gets caught by linters, in practice. And then doing it on purpose is almost as noisy as bubbling it, and sure to raise an eyebrow in code review. My experience is with exceptions in Java/C#, Go errors, and C++ absl::StatusOr. In theory, I'd favor checked exceptions. In practice, I find that I'm always running away from polluting the happy path and coming up with contrived flow when I want to handle/decorate/whatever some errors but not others, and that giving types to checked exceptions becomes a class hierarchy thing that I've also grown to dislike. Both Go and C++/Abseil are indifferent if noisy to me (and C++ annoys me for plenty other reasons). Maybe elsewhere I'd find options better. Maybe.
- wruza 2y agoIsn’t the reason by chance how try-catch works lexically? I find that as another overlooked billion dollar mistake. It’s not that a happy path gets a crack in it, but the absolute monstrosity of a catch ceremony together with a scope torn between two blocks. “try {} catch {} finally {}” should be “[try] { catch {} finally {} }” absolutely everywhere.
- Gibbon1 2y agoI've wondered about just being able to pass an error closure to a function. And if you don't it'll execute a default error closure. void oops(error_t error) { exit(1)}; int sum = addints(a, b, .error = oops);
- AdieuToLogic 2y ago> Needing to constantly check if a specific return value is not nil just seems so sloppy but I have yet to come across a "better" way as it appears to just be a Golang convention. Is there a good post about better ways to handle errors in Go? As others have identified, not in the Go language. In other languages which support disjoint unions with "right-biased" operations, such as Haskell's Either[0], Scala's version[1], amongst others, having to explicitly check for error conditions is not required when an Either (or equivalent) is used. 0 - https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-Either.html https://hackage.haskell.org/package/base-4.20.0.1/docs/Data-... 1 - https://www.scala-lang.org/api/3.x/scala/util/Either.html https://www.scala-lang.org/api/3.x/scala/util/Either.html
- trealira 2y agoRob Pike once talked about dealing with this: https://go.dev/blog/errors-are-values https://go.dev/blog/errors-are-values
- stouset 2y agoNotably this approach is basically completely nonexistent in practice.
- wruza 2y agoHe basically simulates jump to catch by hand. I understand the authority of Rob Pike, but don’t understand why this idea is great. Processes IRL don’t look like write() three times. They look foo(); bar(); baz(); quux();, which weren’t designed to cooperate under a single context. If only there was an implicit err-ref argument which could be filled by some “throw” keyword and the rest would auto-skip to the “catch”.
- kaba0 2y agoNo, go has GOd awful error handling, it is C's errno wrapped in a slightly different syntax.