TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Go's Error Handling Is Perfect

58 pointsby gus_leonelabout 1 year ago

33 comments

hamandcheeseabout 1 year ago
My main issue with errors in go (last time I used it, which was a while ago) is not any of the things mentioned, it&#x27;s that the default pattern you learn provides no stack trace whatsoever in the console.<p>There&#x27;s no error handling in the python code, yet the stack trace will lead you to exactly to where you need to look. To me that is strictly superior when it comes time to debug an error.<p>Edit: maybe not <i>strictly</i> superior, as has been mentioned in replies, wrapped errors with good context at each step does seem like it would be very high SNR.
评论 #39943845 未加载
评论 #39944367 未加载
评论 #39943767 未加载
评论 #39947959 未加载
评论 #39944195 未加载
评论 #39943686 未加载
tombertabout 1 year ago
I think it&#x27;s fallen out of favor, but I always preferred the &quot;Either&quot; monad style of error handling. I think the cool kids call it &quot;railway oriented programming&quot; but I never liked that term. Regardless, it&#x27;s not radically different than the Go style, just instead of returning back two things and checking for `nil`, it&#x27;s wrapped up into an algebraic data type.<p>I like this better, because you have the <i>option</i> of very easily doing the &quot;Go style&quot; of checking for success or failure if you want, but you can also exploit the fact that it&#x27;s a monad and a functor and thus you can just use map and&#x2F;or bind for all the downstream functions, so you don&#x27;t <i>have</i> to explicitly check if you don&#x27;t want to.<p>I feel like this is the best of both world; it still forces you to be explicit (as it&#x27;s compile-time enforced by the type system), but it gives you nice syntax to make the code cleaner.
评论 #39943785 未加载
评论 #39943890 未加载
评论 #40002840 未加载
评论 #39943834 未加载
aeyesabout 1 year ago
The author writes about the Python exception: &quot;Note that for any sufficiently complex program that invokes many dependencies, this stack trace will be so far down the chain that you may not even see where you’re making the call that causes it.&quot;<p>I never encountered this problem with Python, not even in a project with more than 1 million lines of code. Are there any real examples where a Python stack trace is truncated in such a way?<p>It almost sounds like his whole reasoning is based on the assumption that he won&#x27;t be able to understand an error in Python when looking at a stack trace.
评论 #39943762 未加载
评论 #39943752 未加载
spenczar5about 1 year ago
Part of the issue is that when people evaluate a language, they do it in a safe setting: a toy problem, a coding challenge, whatever. In those cases, handling error scenarios is annoying and just slows you down.<p>But when the rubber meets the road and you are trying to write code that consistently works, Go’s verbose but simple error system is really an asset. I used to write a lot more Go and have been writing Python, and I miss Go’s style terribly. Errors as values, the build&#x2F;package tools, gofmt, and the consistent quality of the standard library are what I miss most.
评论 #39943908 未加载
评论 #39943736 未加载
mrkeenabout 1 year ago
I extracted out all the err-checks up to where they&#x27;re handled.<p><pre><code> if err != nil { return nil, err if err := json.NewDecoder(response.Body).Decode(&amp;data); err != nil { return nil, err if err != nil { return err if _, err = reader.Read(); err != nil &amp;&amp; err.Error() != &quot;EOF&quot; { return err if err != nil { if err.Error() == io.EOF { break } return err if err != nil { fmt.Printf(&quot;Failed to fetch data from %s: %v\n&quot;, url, err) continue if err := processCSV(csvFile); err != nil { fmt.Printf(&quot;Error processing CSV: %v\n&quot;, err) </code></pre> Everything just bubbles-up. You&#x27;ve reinvented Exceptions with more boilerplate. Nothing here is &#x27;handled&#x27; beyond that.<p>Although I don&#x27;t read Go, it looks as if main() returns success to its caller even in the failure case.<p>So the Python code &quot;which doesn&#x27;t handle exceptions&quot; exits in failure properly with a good error message and stack trace. The Go code &quot;which handles all aforementioned errors&quot; returns success for a failed operation, printing a less clear error message than the Python.
评论 #39944129 未加载
评论 #39944006 未加载
huimangabout 1 year ago
Go&#x27;s error handling is good, but rust&#x27;s error handling is phenomenal. It just doesn&#x27;t really show in an 18 line program... Rust and Go both force you to acknowledge the possibility of errors in your code, whereas python and ruby, etc, will happily let you ignore them until something blows up.<p>Being able to use `?` in a method that returns `Result&lt;T, Err&gt;` is so elegant and helps readability compared to multiple if err != nil statements in go. In a larger program, ? lets you pass along errors up along the chain until you get to a place where they actually need to be handled, and this lets you consistently deal with them as well. For example, in a slightly more complex example [0], I handle errors on line 162 from monitor_device() (line 176, 181), instead of cluttering up monitor_device() unless I need to. (This was just some prototyping code so please bear with me).<p>[0] <a href="https:&#x2F;&#x2F;github.com&#x2F;andrewzah&#x2F;fst_rs&#x2F;blob&#x2F;master&#x2F;src&#x2F;main.rs#L162">https:&#x2F;&#x2F;github.com&#x2F;andrewzah&#x2F;fst_rs&#x2F;blob&#x2F;master&#x2F;src&#x2F;main.rs#...</a>
joshuamortonabout 1 year ago
&gt; do you want to know when something bad can happen in your program, or don’t you?<p>No actually, I want to know when consequential errors that I can be responsible for happen.<p>I don&#x27;t really care that reading a file could fail (and it can fail in such a variety of ways, there&#x27;s like 3 dozen distinct errors that could be thrown!) because I can&#x27;t do anything about that. But if there are errors that I could recover from, that I would care about.<p>Unfortunately while it&#x27;s possible, very little code distinguishes between error kinds or returns custom errors in go, so your bet is usually parsing error messages, while in Python or rust you have the ability to handle errors that are relevant to you, and let irrelevant ones percolate away.<p>Go on the other hand encourages doing nothing except logging, because it&#x27;s somewhere between difficult&#x2F;unidiomatic and impossible (depending on the libraries your building on top of) to handle only certain kinds of errors.<p>As a concrete example, in that csv parsing code, how do you differentiate between a file not found, file too large, and lacking permissions to read it? The stack overflow consensus is that doing that is unidiomatic, and you should just print the error to the user, which means this is <i>just</i> boilerplate for an exception that will ultimately gracefully end processing and print an error&#x2F;stacktrace to the user. A bunch more typing for precisely the same intent as you get for free in Python or Java.
评论 #39948162 未加载
bhaneyabout 1 year ago
Having an AI generate Python code without error handling, then Go code with error handling (that&#x27;s also ~6x more code, mostly because of all the verbose error handling) is an absolutely wild argument to use to defend Go&#x27;s error handling.
评论 #39943821 未加载
评论 #39943743 未加载
aystaticabout 1 year ago
I don&#x27;t necessarily disagree with the overall point, but I&#x27;m sad the article misses the big advantage to the sum-type solution. It is not just about &quot;being able to collapse `res, err := func()` into `res := func()?`&quot;. With sum types you literally <i>cannot</i> access the Result&#x27;s inner value without performing <i>some</i> kind of error handling. Even if you just `.unwrap()`.<p>from <a href="https:&#x2F;&#x2F;fasterthanli.me&#x2F;articles&#x2F;i-want-off-mr-golangs-wild-ride" rel="nofollow">https:&#x2F;&#x2F;fasterthanli.me&#x2F;articles&#x2F;i-want-off-mr-golangs-wild-...</a><p>&gt; The point is, this function signature makes it impossible for us to access an invalid&#x2F;uninitialized&#x2F;null Metadata. With a Go function, if you ignore the returned error, you still get the result - most probably a null pointer.
lenerdenatorabout 1 year ago
&quot;You’ll mainly see people complain about having to litter their apps with if err != nil, how they feel it makes the code verbose, and how they miss just writing ten very consequential lines one after the other in more dynamic languages like Python or Javascript.&quot;<p>One of the practices you&#x27;ll see better Python devs use is to wrap stuff in try-catch-except blocks, <i>especially</i> around consequential code.
sirwhinesalotabout 1 year ago
Go&#x27;s error handling is very much not perfect. There&#x27;s no reason for it not to have a bunch of syntax sugar for extremely common patterns.<p>See all the syntax sugar C# has for handling nullable types. That could easily be done for error handling as well. Or see Odin which has the exact same error handling mechanism as Go but with proper constructs to make it painless.
peppermint_gumabout 1 year ago
My takeaway from the article is that junior developers are intimidated by stacktraces.
评论 #39943726 未加载
deodarabout 1 year ago
Leaving aside the technical merits of Golang&#x27;s error handling, this article is a straw man. The Python example can be rewritten with error handling. And arguably in this example the Python program is fine.<p>There is no objectively &quot;perfect&quot; approach. I personally prefer getting a stack trace of the point at which the problem occurred. I find that more useful as opposed to getting back an error code which has been propagated and modified through layers of function calls and is often hard to correlate with the original error condition.
amlutoabout 1 year ago
&gt; I also ascribe that issue to the subject of nil in Go, which is a whole other topic, this article is about errors.<p>I’ll bite. Idiomatic Go code, as in the article, does:<p><pre><code> response, err := somethingorother() </code></pre> In the error case, response needs to have a value, and nil serves this purpose. If response were a non-nillable type, this would not work.<p>Result-style types solve this problem.
marcosdumayabout 1 year ago
Wait, the Go error handling on that code has the exact same functionality as the Python one. All it does is making the code about 5 times as long and way less extendable.<p>Way, way less extendable. You&#x27;ll have to completely rewrite the Go code if you want to put it in a library.<p>Now I&#x27;m doubting the author discernment on building that opinion.
voidhorseabout 1 year ago
Either types are probably the best form of error handling.<p>I would agree that complaints about go&#x27;s error handling are really silly because said complaints usually come from people that work in languages with exceptions where, if they choose to handle errors, need to handle them in precisely the same way as in go, just in an even more verbose form.<p>The only reason people coming from these languages don&#x27;t like go is because go actually forces you to either deal with the error or explicitly ignore it rather than just letting your functions propagate failures, which in theory is nice but in practice leads to horrible user experience because the buck wound up not stopping anywhere up the call stack since every caller decided &quot;someone else can handle it&quot;.
zer00eyzabout 1 year ago
I write go.<p>As every programer does you sometimes need to write throw away code. The one off script, a quick tool to clean something up, a loop and a regex to make it cleaner data, aggregating data from two systems into one handy file.<p>Go has this handy way of throwing away an error return. If I have to write something that is trash I skip over the errors. I can ignore missing files or json formatting issues. Granted that code would not pass muster of a code review, it would be apparent what I&#x27;m doing.<p>You can cut corners in go, it just reminds you that your doing it.
gr__orabout 1 year ago
Just as a point of fair comparison, here is their prompt for CSV parsing in Rust (brought to you by Gemini):<p><pre><code> use std::error::Error; use csv::Reader; use reqwest::Client; use serde_json::Value; #[tokio::main] async fn main() -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; { &#x2F;&#x2F; Define the CSV file path let file_path = &quot;data.csv&quot;; &#x2F;&#x2F; Open the CSV reader let mut reader = Reader::from_path(file_path)?; &#x2F;&#x2F; Create a new HTTP client let client = Client::new(); &#x2F;&#x2F; Iterate over each record in the CSV for record in reader.records() { let record = record?; &#x2F;&#x2F; Get the value from the first column let url = record[0].to_string(); &#x2F;&#x2F; Perform GET request let response = client.get(&amp;url).send().await?; &#x2F;&#x2F; Check for successful response if response.status().is_success() { &#x2F;&#x2F; Read response body let body = response.text().await?; &#x2F;&#x2F; Parse JSON let json: Value = serde_json::from_str(&amp;body)?; &#x2F;&#x2F; Process the parsed JSON (e.g., print it) println!(&quot;JSON for URL {}: {}&quot;, url, json); } else { eprintln!(&quot;Error fetching URL: {}&quot;, url); } } Ok(()) } </code></pre> The thing that makes it perfect in Rust, that you can keep the above for a simple script, but if you want to solidify it you just think about how you want to handle the `?`-annotated spots (retries, error-repacking, panics, etc.)
评论 #39949357 未加载
erik_seabergabout 1 year ago
&gt; do you want to know when something bad can happen in your program, or don’t you?<p>Almost everything can fail, and we don&#x27;t need our noses constantly rubbed in it. Knowing <i>how</i> it failed might be interesting, but you&#x27;re on your own to check for error subtypes and it&#x27;s rare to define any.<p>2&#x2F;3 of the code is way too much boilerplate for delegating error handling when it can be automated more reliably.
thefauxabout 1 year ago
Summary: thing people complain about is actually great except when it isn&#x27;t.
conqrrabout 1 year ago
Go&#x27;s error handling is explicit which enforces the need to handle whereas other languages leave it to the developer. More like here&#x27;s the mess take care of it vs everything looks clean but its really swept all under the rug and can bite you later. With growing dependencies and complexity increasing everyday, I&#x27;d rather think of it sooner than later.
评论 #39944217 未加载
评论 #39944062 未加载
评论 #39943761 未加载
verandaguyabout 1 year ago
I&#x27;m not the strongest Go programmer, so maybe my inexperience leads me to still disagree with the ergonomics of &quot;if err != nil&quot; over something like a formal `Result`&#x2F;`Either` type à la Rust&#x2F;Haskell&#x2F;..., but this is a decent bog post.<p>I think using a more structured capital-R Result type, combined with match&#x2F;case makes for better structured code, and I can&#x27;t help but think that `err != nil` is a bit of a smell left over from Go&#x27;s initial decision to avoid doing generics (all of which is alluded to in the writeup).<p>I <i>do</i> disagree with the author using a trivial example to demonstrate that `err != nil` isn&#x27;t that bad, since most code you&#x27;ll work with <i>isn&#x27;t</i> trivial. the errnil pattern obviously isn&#x27;t enough of a problem to deter many companies, which I&#x27;ve always been interested in, since to me it always seemed like a brittle and awkward error handling mechanism.<p>All that aside, `blog.verygoodsoftwarenotvirus.ru` is an <i>excellent</i> domain name.
joshkaabout 1 year ago
In the article<p>&gt; The only opposition I have to the Result type in Go is that we wouldn’t be able to make use of it in the standard library without either breaking backwards compatibility, writing Result variants of existing API calls (so NewRequest, NewRequestWithContext, and NewRequestWithContextAndResult), or issuing new &#x2F;v2 variants of existing packages (like the recently-released math&#x2F;rand&#x2F;v2 package), which then means we’ll have some libraries and programs that use the old style with one return value, some with the new style, and many instances of confused programmers using the wrong one. It would be as close to a Go equivalent of the Python 2&#x2F;3 transition debacle as I think we could manage.<p>I see Go&#x27;s approach and Rust&#x27;s approach as being mostly isomorphic as Go&#x27;s `tuple(result, err)` maps pretty reasonably onto Rusts&#x27;s `either(Err(err), Ok(result))`. If Go implemented a language convention that made these 100% equivalent, there would be no real problem except the battle between the old school and new school code authors as to which one is the right one to use in their code.<p>Caveat - I am not a Go programmer, so I have no idea how feasible this is as an addition to the language proper. If you wanted to write Rust like you do go, it would be as simple as the following (cursed):<p><pre><code> impl From&lt;Result&lt;T, E&gt;&gt; for (Option&lt;T&gt;, Option&lt;E&gt;) { fn from(result: Result&lt;T, E&gt;) -&gt; (Option&lt;T&gt;, Option&lt;E&gt;) { match result { Ok(value) =&gt; (Some(value), None), Err(error) =&gt; (None, Some(error)), } } } </code></pre> then<p><pre><code> let (val, err) = someFallibleCall().into(); if val.isSome() &#x2F;&#x2F; something good with val.unwrap() } else &#x2F;&#x2F; something bad with err.unwrap() } </code></pre> (noting of course this would be intentionally unidiomatic rust - real code would probably use match (val, err)) for this.
01HNNWZ0MV43FFabout 1 year ago
&gt; The only opposition I have to the Result type in Go is that we wouldn’t be able to make use of it in the standard library without either breaking backwards compatibility, or (breaking backwards compatibility)<p>I&#x27;d say then that it&#x27;s only as perfect as you can be while being compatible with existing Go code.
hnrodeyabout 1 year ago
False dichotomy; cart before the horse?<p>My general approach is validate inputs before running &quot;the thing&quot;.<p>Check if file exists<p>Parsing CSV file is moved to another class, do more validations on data<p>HTTP calls, if failed, shouldn&#x27;t eject the entire algo.<p>FWIW, that Go code might be &quot;safer&quot; but it hurt my eyes.
reactordevabout 1 year ago
I&#x27;ve come to the same conclusion. After teaching several other developers how to Golang, coming from the same general background as me, I found that as we grew in lines of code that Go&#x27;s error value system was actually really really useful and helped develop more defensive programming styles. Being able to define your own errors and use those instead of instantiating a string every time or having non-standard error string formats.<p>It gets in your way at the beginning but once you get beyond a module or two, it&#x27;s quite useful, novel, and under appreciated.
nikhizzleabout 1 year ago
Much more in the camp of exhaustive pattern matching such as in Scala or Elixir. Makes my code much cleaner, and I avoid if statements and loops.
sarahlwalksabout 1 year ago
Well, maybe I’m not cool enough to appreciate it, and maybe I’m more accustomed to the style of other languages, but if I see more than a few ifs in a file, it feels like a code smell, and it feels like some refactoring might be good. But I’m hearing that this profusion of error checking “if“ statements is the go way.<p>To me, the ideal in a language is to assume that everything will go right, to enunciate the core intention of the program, function, etc., and to handle errors somewhere else, maybe below the main code or something like that. Mixing it all in right there with the code, even if we end up concluding that this turns out to be the best way to deal with things, seems visually confusing to me when reading the code.
spenczar5about 1 year ago
I liked this article, and the discussion in these comments. I don’t understand why it was flagged. Is there a way to find out? Perhaps @dang summons an explanation?
cess11about 1 year ago
I prefer switching on Erlang&#x27;s (Elixir&#x27;s, &amp;c.) tuples.<p><pre><code> {:ok, result} {:error, reason}</code></pre>
raincoleabout 1 year ago
This article basically just says two things:<p>1. Error handling is better than no error handling at all<p>2. Go is designed to be like Go not Rust<p>Yeah, your typical Go article...
JaDoggabout 1 year ago
What is with the site - `very good software not virus . ru`
评论 #39943772 未加载
mrsilencedogoodabout 1 year ago
No mention of `errors.Is` or error wrapping? IMO that&#x27;s where the pain starts.<p>I definitely agree that in many programs, when an error happens, you&#x27;re done, you just blast up the stack and then the error message goes into a sentry report or gets logged etc.<p>But it&#x27;s also still quite common to want to treat some error conditions as soft errors, or otherwise handle them differently. Now you&#x27;re doing errors.Is and engaging with the poorly structured world of error wrapping trees.<p>Here&#x27;s one I hit recently. One of my pet peeves is error messages like &quot;unsupported thing&quot;. Ok, what thing was unsupported? Very often people just omit this stuff because it requires you to now cat strings, which is sometimes hard or cumbersome depending on the language. So, I changed my code to have the error be &quot;unsupported thing: &lt;thing&gt;&quot;.<p>However, up the call stack, I want to handle ErrUnsupportedThing too. Because maybe there&#x27;s some reasonable thing I can do to help the user make the thing supported. So I&#x27;m calling `errors.Is`. But wait, now I need an `ErrUnsupportedThing` variable to refer to. But each call to fmt.Error creates a new error that isn&#x27;t `errors.Is`-equivalent to each other.<p>I ended up reconciling this by making `ErrUnsupportedThing` just be &quot;unsupported thing&quot; and then I wrap it by doing fmt.Errorf(&quot;%w: %v&quot;, ErrUnsupportedThing, thingType).<p>But now what if I want to say &quot;ErrUnsupportedThing: &lt;A sub-error that occurred&gt;&quot;? Well, hope you&#x27;re using a recent version of go, because &quot;%w: %w&quot; only works in go 1.20 and later.<p>Also the fact that all errors are just &quot;error&quot; instead of being typed errors tends to make people a lot lazier about their error handling in general. In java, if I get a ThingNotSupportedException, I expect to be able to find a getUnsupportedThingType() so that I can explicitly pull it off the string without needing to parse. That&#x27;s easy in Java, it&#x27;s a subclass of exception. In go, now you&#x27;re writing a &quot;subclass&quot; struct to implement error&#x27;s interface, tacking on extra struct fields and stuff, and since nobody ever actually types their return values more explicitly than `error`, you&#x27;re now doing casting&#x2F;duck-typing to pull that information out. Etc.<p>I guess let me put it this way. Go errors are good for 80% of errors and fine for 95%. That&#x27;s great. But the easy code is already easy! What irks me is when it breaks down when I get to the code that&#x27;s actually a bit tricky to handle errors for. The type system, the error system, and everything is just like &quot;wow good luck you&#x27;re on your own&quot;. Whereas python&#x2F;java&#x2F;etc have my back here.
评论 #39948201 未加载