[HN Gopher] (On | No) Syntactic Support for Error Handling
       ___________________________________________________________________
        
       (On | No) Syntactic Support for Error Handling
        
       Author : henrikhorluck
       Score  : 269 points
       Date   : 2025-06-03 16:18 UTC (6 hours ago)
        
 (HTM) web link (go.dev)
 (TXT) w3m dump (go.dev)
        
       | codr7 wrote:
       | I just wish the could stabilize the compiler before piling on
       | more features.
       | 
       | I regularly run into internal compiler errors these days for
       | pretty normal looking code.
       | 
       | It's getting to the point where I'm reluctant to invest more time
       | in the language right now.
       | 
       | UPDATE: See comment below for full error message and a link to
       | the code.
        
         | johnfn wrote:
         | In that case, you might enjoy this part of the article:
         | 
         | > For the foreseeable future, the Go team will stop pursuing
         | syntactic language changes for error handling
        
         | Thaxll wrote:
         | I've never seen a single internal compiler issue in 10 years of
         | working in Go.
         | 
         | What error are you talking about?
        
           | kbolino wrote:
           | Not OP, but I found a compiler bug in the initial release of
           | 1.18, though it was also quickly fixed:
           | https://github.com/golang/go/issues/51733
        
           | codr7 wrote:
           | Me neither until the latest version.
           | 
           | Internal compiler error, type2 something, unknown line
           | number.
           | 
           | I'll have to look it up.
           | 
           | Downvotes for reporting an error, very classy HN.
           | 
           | UPDATE: See comment below for full error message and a link
           | to the code.
        
         | shivamacharya wrote:
         | If you do stuff the language doesn't support, it's not the
         | languages problem.
        
           | codr7 wrote:
           | I did nothing weird, played around a bit with the new
           | iterators.
           | 
           | Internal compiler errors are very much the implementation's
           | problem.
           | 
           | UPDATE: See comment below for full error message and a link
           | to the code.
        
             | shivamacharya wrote:
             | I only say this because I have yet to encounter a single
             | internal compiler error in years of writing Go
             | professionally. I can't conceive of the kind of code one
             | must be writing for internal compiler errors to be a
             | repeated issue.
        
               | codr7 wrote:
               | So here's the complete error message:
               | 
               | <unknown line number>: internal compiler error:
               | unexpected types2.Invalid
               | 
               | Please file a bug report including a short program that
               | triggers the error. https://go.dev/issue/new
               | 
               | And here's the code that triggers it:
               | 
               | https://github.com/codr7/shi-go/blob/main/src/shi/call.go
               | 
               | The code is never referenced in the project, but running
               | make in the project root with this file in it triggers
               | the error. Remove the file and the error disappears.
               | 
               | Happy now?
        
               | kiitos wrote:
               | Paste exactly what commands you run and exactly their
               | output, including exactly the rev of the repo you're
               | running them in.
               | 
               | This code is buggy from tip to tail, my goodness!
               | Starting with Make targets like `go test src/tests/*`
               | (invalid syntax), no `gofmt` formatting, everything in
               | https://github.com/codr7/shi-go/blob/main/src/shi/vm.go,
               | no tests anywhere -- really looks like you're programming
               | against a language spec that you've invented yourself,
               | maybe influenced by Go, but certainly not valid Go as
               | defined!
               | 
               | > Remove the file and the error disappears
               | 
               | Remove the file and the code no longer compiles, because
               | it contain definitions that are used by other code in the
               | package.
        
       | stefanos82 wrote:
       | I have hard time understanding why they didn't go with
       | func printSum(a, b string) error {             x :=
       | strconv.Atoi(a) or {                 return error             }
       | y := strconv.Atoi(b) or {                 return error
       | }             fmt.Println("result:", x + y)             return
       | nil         }
       | 
       | or something along these lines...
        
         | wredcoll wrote:
         | Oh cool, you've reinvented perl.
        
         | righthand wrote:
         | > Unfortunately, as with the other error handling ideas, this
         | new proposal was also quickly overrun with comments and many
         | suggestions for minor tweaks, often based on individual
         | preferences. Ian closed the proposal and moved the content into
         | a discussion to facilitate the conversation and to collect
         | further feedback. A slightly modified version was received a
         | bit more positively but broad support remained elusive.
         | 
         | > After so many years of trying, with three full-fledged
         | proposals by the Go team and literally hundreds (!) of
         | community proposals, most of them variations on a theme, all of
         | which failed to attract sufficient (let alone overwhelming)
         | support, the question we now face is: how to proceed? Should we
         | proceed at all?
         | 
         | > We think not.
        
         | shivamacharya wrote:
         | If you do this, returning the error last is now part of the
         | language rather than a convention. You'd be making a pretty
         | large language change.. and for what? One line of code that's
         | already folded away by any modern editor?
        
         | alanbernstein wrote:
         | Seems like it's only worth the cost of the change if it removes
         | all three verbose lines per check, this only removes one.
        
         | arp242 wrote:
         | You need to assign the error to a variable, probably. So it
         | would have to be more something like:                 n :=
         | strconv.Atoi(s) or |err| {           return fmt.Errorf("foo:
         | %w", err)       }            n := strconv.Atoi(s) or |err| {
         | if !errors.Is(err, pkg.ErrFoo)               return
         | fmt.Errorf("foo: %w", err)           }       }
         | 
         | Just "error" (which shadows the built-in type) won't really
         | work.
         | 
         | I'm just making up syntax here to illustrate the point; doesn't
         | look too brilliant to me. A func might be a bit more "Go-like":
         | n := strconv.Atoi(s) or func(n int, err error) {
         | return fmt.Errorf("foo: %w", err)       }
         | 
         | All of this is kind of a moot point at Robert's blog post says
         | that these proposals won't be considered for the foreseeable
         | future, but IMHO any error handling proposal should continue to
         | treat errors as values, which means you should be able to use
         | fmt.Errorf(), errors.Is(), mylogger.Error(), etc.
        
       | 827a wrote:
       | IMO: The position of "we aren't sure what the right approach to
       | improvement is so we aren't going to do anything" has killed far
       | bigger and more important projects, companies, and even
       | countries, than Golang. Adapt or die; the world isn't going to
       | wait for you.
       | 
       | (I love the Go team, and appreciate everything they do. I'm just
       | sad to see a language I used to love fail to keep pace with many
       | of the other options out there today.)
        
         | justin66 wrote:
         | Go adoption has only increased over time.
        
           | 827a wrote:
           | There's no methodologically sound way to measure its
           | popularity available to most people to make an argument on
           | its popularity one way or the other. That's why I didn't, and
           | that's why you shouldn't either.
        
             | justin66 wrote:
             | If you say so, but the concern you floated (The position of
             | "we aren't sure what the right approach to improvement is
             | so we aren't going to do anything" has killed far bigger
             | and more important projects, companies, and even countries,
             | than Golang. ) is fundamentally silly, given how long Go
             | has been in business and seemingly thriving while applying
             | this approach to a few select, controversial features.
             | 
             | If it was going to be killed by this approach, it would now
             | be dead.
        
         | MeetingsBrowser wrote:
         | Changing too quickly is a much bigger problem. It may not be
         | ideal, but I think leaning towards being slow makes sense in
         | this context.
         | 
         | Every person/company using Go chose to use it knowing how
         | errors are handled.
         | 
         | Each new way of error handling seems to upset a large number of
         | users, some of which may not have chosen Go had the newer
         | system been in place originally.
         | 
         | If it is impossible to know which choice is correct, at least
         | everyone has some baseline level of acceptance for the status
         | quo.
        
           | 827a wrote:
           | Changing too quickly is not a problem. Changing too quickly
           | can _lead_ to problems.
           | 
           | I don't agree that the problems it leads to are bigger
           | problems than stagnation. I also don't believe they're
           | smaller problems; sorting the problems by size is
           | intractable, as it is situation dependent.
           | 
           | The challenge is in the definition of "too quickly"; if
           | fifteen years of stagnation in addressing more productive
           | error handling is the "right pace" of innovation, or lack-
           | there-of; is twenty years? Thirty years? One hundred years?
           | How do you decide when the time is right? Is the Go team just
           | waiting out the tides of the Vox Populi, and maybe one day a
           | unified opinion from the masses will coalesce?
           | 
           | That's weak.
        
         | acedTrex wrote:
         | In this particular case the argument seems to be that there is
         | not even consensus improvement is NEEDED. If you cant even
         | agree on that then how do ever hope to agree to a change in the
         | general sense.
        
         | arp242 wrote:
         | > Adapt or die
         | 
         | It's not my impression Go is dying. Seems rather overblown.
         | 
         | And this "but $other_lang has it! You must have it! Adapt or
         | die!" type of reasoning is how you end up with C++.
        
           | 827a wrote:
           | I never said Go is dying. I said go must adapt, or it will
           | die. That's future-tense.
           | 
           | Sure, you can end up with C++ (which is still by some
           | measures the most popular programming language in the world,
           | so that's not a bad place to be). You can also end up with
           | Rust, or Kotlin, or any one of the _literally every other
           | programming languages in any ranking 's Top 30_, all of which
           | have more ergonomic error handling.
           | 
           | A better example in the opposite direction is Java: Its a
           | language that spent years refusing to adapt to the ever-
           | changing needs of software engineers. Its legacy now. That is
           | not Go's present, but it is Go's future at its current pace.
           | Still powering a ton of projects, but never talked about
           | except in disdain like "ugh that Go service is such tech
           | debt, can we get time modernize it next sprint". I don't want
           | that for the language.
        
       | righthand wrote:
       | Hopefully this is the final nail in the coffin for all the "but I
       | have to handle my errors" whining.
        
         | aystatic wrote:
         | I don't know that there's whining about "having to handle
         | errors" in principle, it's pretty clearly a complaint with the
         | syntax and semantics of doing so
         | 
         | Some languages even make omitting error handling impossible!
         | (e.g. Result sum types). None have anywhere near the amount of
         | "whining" Go seems to attract
        
       | maxwellg wrote:
       | This is the right move for Go. I have grown to really love Go
       | error handling. I of course hated it when I was first introduced
       | to the language - two things that changed that:
       | 
       | - Reading the https://go.dev/blog/errors-are-values blog post
       | (mentioned in the article too!) and really internalizing it.
       | Wrote a moderately popular package around it -
       | https://github.com/stytchauth/sqx
       | 
       | - Becoming OK with sprinkling a little `panic(err)` here and
       | there for truely egregious invalid states. No reason forcing all
       | the parent code to handle nonsense it has no sense in handling,
       | and a well-placed panic or two can remove hundreds of error
       | checks from a codebase. Think - is there a default logger in the
       | ctx?
        
         | masklinn wrote:
         | This is just sad. Neither of your supports have anything to do
         | with how dismal Go's error handling is, and neither would be
         | worsened in any way by making it better. If anything they would
         | be improved.
        
         | pas wrote:
         | Even PHP has better error handling with the levels, and the @
         | (at operator) to suppress errors at a callsite.
         | 
         | Even bash has -e :)
        
         | bravesoul2 wrote:
         | Me too. I'll take the higher Loc for the greater certainty of
         | what is going on.
         | 
         | I thought it was clever in C# years ago when I first used to to
         | grok all the try/catch/finally flows including using and nested
         | versions and what happens if an error happens in the catch and
         | what if it happens in the finally and so on. But now I'd rather
         | just not think about that stuff.
        
       | marcoc wrote:
       | Now that I think about it, are there any color schemes or
       | extensions that highlight the error handling logic differently so
       | that one can better focus on the "main" logic flow of the code
       | while the error handling logic is still there?
        
         | Refreeze5224 wrote:
         | I would love something like this, and if it exists, I've not
         | come across it. Offloading a way of differentiating error
         | handling syntax vs. normal code to the IDE seems like a nice
         | way of handling this issue.
        
         | renewiltord wrote:
         | Goland folds it visually partially
         | https://github.com/golang/vscode-go/issues/2311
        
         | rollcat wrote:
         | You would have to infer the intent of the code. One example,
         | from a small project I'm working on:                   $ find .
         | -name "*.go" | xargs grep 'if err !=' | wc -l             242
         | $ find . -name "*.go" | xargs grep 'if err ==' | wc -l
         | 12
         | 
         | So about 5% of the error checking code is about handling the
         | edge cases, where we are very much interested in what the error
         | actually is, and need to handle those conditions carefully.
         | 
         | If you discard that as "error handling noise", you're in for a
         | bug. Which is, by the way, perhaps the worst side-effect of
         | verbose, repetitive error handling.
         | 
         | Apropos syntax highlighting: many themes in regular use
         | (certainly most of the defaults) choose a low-contrast color
         | for the comments. The comments are often the most important
         | part of the code.
        
       | Refreeze5224 wrote:
       | I don't really understand this decision. They know from developer
       | surveys that verbose and repetitive error handling is literally
       | the number 1 issue. Seeking the perfection of syntax that
       | everyone agrees on seems to be the enemy of providing _some_ good
       | alternative to the status quo.
       | 
       | Their comment about providing some new syntax and people being
       | forced to use it seems off base to me. It's nice to not have
       | multiple ways of doing things, but having only 2 when it comes to
       | error handling does not seem like a big deal. I imagine people
       | will just use their preference, and a large percentage of people
       | will have a less verbose option if they want it.
        
         | munificent wrote:
         | _> They know from developer surveys that verbose and repetitive
         | error handling is literally the number 1 issue._
         | 
         | Agreement on a problem does not imply agreement on a solution.
         | 
         | It's not about perfection. It's about not having a solution
         | that gets anywhere near a majority approval.
         | 
         | Let's say your neighborhood has an empty plot of land owned by
         | the city that is currently a pile of broken chunks of concrete,
         | trash, and tangled wire. It's easy to imagine that there is
         | unanimous agreement by everyone in the neighborhood that
         | something better should be placed there.
         | 
         | But the parents want a playground, the pet owners want a dog
         | park, the homeless advocates want a shelter, the nature lovers
         | want a forest, etc. None of them will agree to spend their tax
         | dollars on a solution that is useless to them, so no solution
         | wins even though they all want the problem solved.
        
           | Grikbdl wrote:
           | Even if people in your example couldn't agree on a particular
           | alternative, the outcome still is a less attractive area,
           | maybe some will move out and fewer people move in. So, _any_
           | solution would be better than the status quo - and they all
           | would probably agree on that.
           | 
           | The lack of a good error handling story to a lot of people
           | puts go in a mental trash bin of sorts. Similar (but
           | different) reasons eg Java goes to a mental trash bin. I
           | think leaving this issue unhandled will only make go look
           | worse and worse in comparisons as the programming language
           | landscape evolves. It might take 10 or 20 years but it'll
           | always be unique in having "trash bin worthy" error handling.
           | (this can perhaps be argued - maybe exceptions are worse, but
           | at least they're standard).
        
             | munificent wrote:
             | _> So, any solution would be better than the status quo -
             | and they all would probably agree on that._
             | 
             | The point is that people do _not_ agree that _any_ solution
             | is better than the status quo. In my analogy, if
             | redeveloping that plot of land is quite expensive in tax
             | dollars, people will prefer it be left alone completely so
             | that money can be spent elsewhere than have it squandered
             | on a  "solution" that does nothing for them.
             | 
             | Likewise in Go, adding language features has a quite large
             | cost in terms of cognitive load, decision load,
             | implementation cost, etc. After many many surveys and
             | discussions, it's clear that there is no consensus among
             | the Go ecosystem that _any_ error handling strategy is
             | worth that cost.
        
             | throwaway894345 wrote:
             | In the analogy we might suppose everyone agrees that there
             | is a problem and any solution is better than the status
             | quo, but that's extremely unlikely in the case of Go. In my
             | experience discussing this issue with Go users and critics,
             | a lot of Go users find the status quo to be minimally
             | chafing.
             | 
             | > The lack of a good error handling story to a lot of
             | people puts go in a mental trash bin of sorts. ... It might
             | take 10 or 20 years but it'll always be unique in having
             | "trash bin worthy" error handling. (this can perhaps be
             | argued - maybe exceptions are worse, but at least they're
             | standard).
             | 
             | Remember that the context is _syntactic_ error handling
             | proposals, not proposals for error handling generally--the
             | maintainers are saying they 're only going to close syntax-
             | only error handling proposals. While I have no doubt that
             | there are lots of people who write of Go for its error
             | handling syntax alone, I don't see any reason why a
             | language community should prioritize the opinions of this
             | group.
             | 
             | Additionally, while I have plenty of criticism for Go's
             | error handling, I can't take "Go's error handling is 'trash
             | bin worthy'" seriously. There are no languages that do
             | error handling well (by which I mean, no implicit control
             | flow and one obvious way to create errors with appropriate
             | error context, no redundant context, clear error messages,
             | etc). Go and Rust probably both give you the tools
             | necessary to do error handling well, but there's no
             | standard solution so you will have issues integrating
             | different libraries (for example, different libraries will
             | take different approaches to attaching error context, some
             | might include stack traces and others won't, etc). It's a
             | mess across the board, and verbosity is the least of my
             | problems.
        
           | pixl97 wrote:
           | >It's about not having a solution that gets anywhere near a
           | majority approval.
           | 
           | You'll never get it in any non-gamed environment.
           | 
           | In democratic voting in FPtP systems if there isn't a
           | majority winner you'll take the top two and go to runoffs
           | forcing those that are voting to pick the best of the bad
           | choices.
           | 
           | This is the same thing that will typically happen in the city
           | you're talking about, hence why most democracies are
           | representative and not direct.
        
         | arp242 wrote:
         | > They know from developer surveys that verbose and repetitive
         | error handling is literally the number 1 issue.
         | 
         | According to 13% of respondents. So yes, it's the "#1 issue",
         | but also not by a huge overwhelming majority or anything.
        
           | pixl97 wrote:
           | Honestly this is only because they made a bad survey. A
           | ranked choice is better.
           | 
           | Lets say you have 5 choices. You give each choice a voting
           | weight of 1 (not an issue) to (5 biggest issue). You only get
           | to pick a weight once.
           | 
           | So in this type of voting even if everybody put error
           | handling and #4 it could still win by a large margin if the 5
           | values were spread out over other concerns.
        
         | ignoramous wrote:
         | > _I don 't really understand this decision._
         | 
         | The decision is disappointing, but understandable.
         | 
         | The blog post attempted to explain it, but it comes down to: A
         | lot of energy has been expended without the community _and_ the
         | core team reaching any form of consensus. The current error
         | handling mechanism has entrenched itself as idiomatic for a
         | very long time now. And since the promising ones among the
         | various proposals involve language changes, the core team,
         | which is stretched already, isn 't willing to commit to it _at
         | this time_ , especially given the impact.
        
           | nimish wrote:
           | This paragraph alone is fundamentally better than the page or
           | so of text in the blog post.
           | 
           | I'm not sure what it is about the style of technical writing
           | I've seen lately but just directly getting to the point
           | versus trying to obfuscate the thesis on a potentially
           | controversial topic is increasingly rare
        
         | shadowgovt wrote:
         | Python is a bit of a counter-example these days. I think
         | they're in a good place right now, but it's hard to argue
         | they've stuck to the premise of "There should be one-- and
         | preferably only one --obvious way to do it."
         | 
         | - I need to do string interpolation: am I using f-strings or
         | `string.format` or the modulo operator?
         | 
         | - I need to do something in a loop. Well, I can do that. But I
         | could also just do a list or sequence comprehension... Or I
         | could get fancy and combine the two!
         | 
         | And such and so-on, but these are the top examples.
         | 
         | Changing these things _strictly adds_ cognitive load because
         | you will eventually encounter libraries that use the other
         | pattern if you 're familiar with the one pattern. And at the
         | limit of what this can do to a language, you get C++, where the
         | spec exceeds the length of the Bible and many bits of it create
         | undefined behavior when used together.
         | 
         | I think Go's project owners are very justifiably paranoid about
         | the consequences of opening the Pandora's box on new features,
         | even if it means users have to deal with some line-noise in the
         | code.
        
         | yandrypozo wrote:
         | people that answer surveys don't represent all Go developers,
         | many of us are fine with the error status quo
        
           | hathawsh wrote:
           | What I wonder about is the pool of _potential_ Go developers.
           | Is the error handling issue serious enough to stop developers
           | from even considering Go? Go would have been an obviously
           | better choice than most languages 30 years ago, but today
           | there are many more good options.
        
             | tuckerman wrote:
             | If you shake things up so much that users who previous
             | dismissed your language are interested, you might also be
             | making a big enough change that your current users look
             | around as well. The pool of prospective new language users
             | is always large but they won't join a language that is
             | dying because it churned all its existing users and package
             | maintainers.
             | 
             | I say this as someone that gets a very bad taste in my
             | mouth when handling errors in go but use it a fair bit
             | nonetheless.
        
             | homebrewer wrote:
             | If you're writing the universe, maybe. There aren't that
             | many competitors when you take the ecosystem into
             | consideration. It is the only reason I tolerate Go where it
             | makes (some) sense -- mostly CLI utilities that are too
             | complicated for bash.
        
             | arp242 wrote:
             | Every language has the _potential_ to attract new
             | developers if they change /add just this one thing.
        
             | throwaway894345 wrote:
             | > Is the error handling issue serious enough to stop
             | developers from even considering Go
             | 
             | If it is, then I suspect those developers are going to have
             | a thousand other non-overlapping reasons not to consider
             | Go. It seems like a colossal waste of time to court these
             | people compared with optimizing Go for the folks who
             | already are using it or who reasonably might actually use
             | it (for example, people who would really like to use Go,
             | but it doesn't support their platform, or it doesn't meet
             | some compliance criteria, or etc).
        
               | pixl97 wrote:
               | Ah, voting by survivorship bias.
        
               | throwaway894345 wrote:
               | No, this is not an example of survivorship bias. Here's
               | the wiki link for your reference.
               | https://en.wikipedia.org/wiki/Survivorship_bias
        
               | pixl97 wrote:
               | No this is an example of survivorship bias.
               | 
               | Let's say Go has such bad error handling that it becomes
               | the number one reason people don't use it.
               | 
               | The people left that do use it will be the ones that
               | don't care about error handling. Hence you're asking the
               | people that don't care versus 90% of the audience you've
               | already lost.
        
         | teeray wrote:
         | > ...having only 2 when it comes to error handling does not
         | seem like a big deal. I imagine people will just use their
         | preference...
         | 
         | I foresee endless PR arguments about whether err != nil is the
         | best practice or whatever alternative exists. Back-and-forth
         | based on preference, PRs getting blocked since someone is using
         | the "other" style, etc. Finally the org is tired of all this
         | arguing and demands everyone settle on the one true style
         | (which doesn't exist), and forces everyone to use that style.
         | That is where the "forced to use it" comes from.
         | 
         | From the early days, Go has taken principled stands on matters
         | like this, striving for simplicity and one way to do something.
         | For example, `go fmt` cut through all the tabs vs. space
         | nonsense by edict. "Gofmt's style is no one's favorite, yet
         | gofmt is everyone's favorite."
        
         | nepthar wrote:
         | Yeah, this is the single biggest reason I avoid go - I just
         | don't want to clutter my "happy path" logic. It makes things
         | harder to reason about.
         | 
         | "Errors are values", sure. Numbers are values and Lists are
         | values. I use them differently, though.
         | 
         | I wonder if there could be "stupid" preprocessing step where I
         | could unclutter go code, where you'd make a new token like
         | "?=", that got replaced before compile time. For instance, "x
         | ?= function.call();" would expand to "x, err :=
         | function.call(); if (err != nil) return err;"
        
           | skydhash wrote:
           | There's no happy path in programming. Errors are just some of
           | the many states in the code and the transition to them
           | doesn't disappear magically because you chose to not specify
           | them. Actually returning an error is just a choice, you can
           | chose to handle the situation, maybe log the error and go on
           | your merry way. Or you panic() and cancel the whole call
           | stack back to a recover(). I like Go because it forces you to
           | be explicit.
        
         | jiehong wrote:
         | The language was first designed without consensus and then
         | released. It was used anyways.
         | 
         | But now a sort of democracy is required for changes. I'm not
         | sure this is necessary.
        
           | wrs wrote:
           | The module system shows the Go core team will make unilateral
           | changes if they feel like it. But if I read this correctly,
           | there's not a consensus even amongst the Go team on what to
           | do here.
        
             | throwaway894345 wrote:
             | According to TFA, it's not quite "if they feel like it",
             | it's when there isn't consensus among the community but (1)
             | it's clear that there is a problem and (2) the Go
             | architects can agree on a path forward.
        
       | rco8786 wrote:
       | This feels like it's making it worse lol
        
       | rollcat wrote:
       | Snip:                   if err != nil {             return
       | fmt.Errorf("invalid integer: %q", a)         }
       | 
       | Snip. No syntax for error handling is OK with me. Spelling out
       | "hey I actually do need a stack trace" at every call site isn't.
        
         | skydhash wrote:
         | You actually don't. In mistakes #52 "Handling an error twice"
         | from the book "100 Go Mistakes and How to Avoid Them" by Teiva
         | Harsanyi, the recommendation is to either return an error to
         | the caller or handle the situation (and maybe logging it).
         | Sometimes you wnat some extra information as to why it's an
         | error, so you wrap it.
         | 
         | Go forces you to be explicit about error handling. Java syntax
         | is not that much better. JavaScript, Kotlin, Swift,... is more
         | about avoiding null pointer exception than proper error
         | handling.
        
           | rollcat wrote:
           | I return the error to the caller. The caller returns it to
           | their caller. 5 frames up, _someone_ gets a [syscall.EINVAL],
           | and _has_ to figure out what to do about it. Perhaps it 's
           | time to log it?
           | 
           | If I had to write my own "100 mistakes" book, "assuming the
           | callee knows what to do" would be somewhere in the top 20,
           | down below "I won't need to debug this".
        
             | skydhash wrote:
             | It's all about designing software. The callee is the one
             | encountering the error, not any of the caller up in the
             | stack trace. Somewhere in the call chain, there's a need to
             | take a decision.
             | 
             | So you, as the developer, decide where that needs to be. It
             | may be at the callee level (like an exponential retry) or
             | at the caller level (display an error message). In the
             | later case, you may want to add more information to the
             | error data block, so that the caller my handle the
             | situation appropriately. So if you want tracing, you just
             | need to wrap the error and returns it. Then your logging
             | code have all the information it needs: like
             | [error saving file [permission error [can't access file]]]
             | 
             | instead of just [syscall.EINVAL].
        
               | rollcat wrote:
               | > So if you want tracing, you just need to wrap the error
               | and returns it.
               | 
               | Here we go, fifth time we're both spelling this one out.
               | This thread is now a meta-self-joke.
        
       | zb3 wrote:
       | Thankfully repetitive language is less of a problem now that we
       | have AI. The current syntax is just "add error handling to this
       | function" :)
        
         | Jtsummers wrote:
         | You're getting downvoted, but this was what tptacek basically
         | wrote about. Key points from his blog are that LLMs are good at
         | tedium, and Go's structure is highly repetitive and amenable to
         | LLM generation. The error handling tedium is probably part of
         | why it's highly repetitive.
         | 
         | > I work mostly in Go. I'm confident the designers of the Go
         | programming language didn't set out to produce the most LLM-
         | legible language in the industry. They succeeded nonetheless Go
         | has just enough type safety, an extensive standard library, and
         | a culture that prizes (often repetitive) idiom. LLMs kick ass
         | generating it.
         | 
         | https://news.ycombinator.com/item?id=44163063 - 2386 comments
        
         | homebrewer wrote:
         | Writing it was never the problem if you're using proper tools,
         | e.g. an actual IDE (IDEA does fine), or at least a snippet
         | manager for your text editor. Inserting a wrapping error
         | handling snippet requires two key presses.
         | 
         | It's reviewing mountains of that crap that's the problem,
         | especially if there are non-trivial cases hidden in there, like
         | returning the error when `err == nil` (mentioned by others in
         | this thread).
        
       | whstl wrote:
       | I used to hate the repetitive nature of Go's error handling until
       | I was burned by bad/mysterious error messages in production.
       | 
       | Now my error handling is not repetitive anymore. I am in peace
       | with Golang.
       | 
       | However I 100% get the complaint from the people who don't need
       | detailed error messages.
        
       | arp242 wrote:
       | > Of course, there are also valid arguments in favor of change:
       | Lack of better error handling support remains the top complaint
       | in our user surveys.
       | 
       | Looking at that survey, only 13% mentioned error handling. So
       | that means 87% _didn 't_ mention it. So in that sense, perhaps
       | not too much weight should be given to that?
       | 
       | I agree the verbosity fades into the background, but also feel
       | _something_ better can be done, somehow. As mentioned there 's
       | been a gazillion proposals, and some of them seem quite
       | reasonable. This is something where the original Go design of "we
       | only put in Go what Robert, Ken, and Rob can all agree on" would
       | IMHO be better, because these type of discussions don't really
       | get a whole lot better with hundreds of people from the interwebz
       | involved. That said, I wasn't a fan of the try proposal and I'm
       | happy it didn't make it in the language.
       | 
       | And to be honest in my daily Go programming, it's not that big of
       | a deal. So it's okay.
        
         | smw wrote:
         | "biggest challenge", it's not multiple choice
        
       | throwaway71271 wrote:
       | Everything is fine
       | 
       | I dream if err, if err dreams me.
        
       | pie_flavor wrote:
       | You draw up a list of checkboxes, you debate each individual
       | point until you can check them off, you don't uncheck them unless
       | you have found a showstopping semantics error or soundness hole.
       | Once it is complete it is implemented and then everyone who had
       | opinions about whether it should be spelt `.await` or `/await` or
       | `.await!()` vanishes back into the woodwork from whence they
       | came. Where's the disconnect?
       | 
       | Rust works like this. Sometimes an issue can be delayed for over
       | a decade, but _eventually_ all the boxes are checked off and it
       | gets stabilized in latest nightly. If Go cannot solve the single
       | problem everyone immediately has with the language, despite
       | multiple complete perfect proposals on how to do it, simply
       | because they cannot pick between the proposals and are waiting
       | for people to stop bikeshedding, then their process is a farce.
        
         | hu3 wrote:
         | > Go cannot solve the single problem everyone immediately has
         | with the language...
         | 
         | What? Survey says 13% mentioned error handling.
         | 
         | And some people actually do prefer it as is.
         | 
         | https://go.dev/blog/survey2024-h1-results
        
           | judofyr wrote:
           | 13% mentioned that error handling was the _biggest_ challenge
           | with using Go. This was not a multiple choice question, but
           | you had to pick _one_ answer. We don 't know how many people
           | would consider it challenging. (This is typically why you
           | have a 1-10 scale per choice.)
        
           | joaohaas wrote:
           | This doesn't mean the rest of the 87% enjoy it. Honestly, I'd
           | rather the next survey included a question "are you satisfied
           | with the current error handling approach"
        
         | arccy wrote:
         | and this is how rust gains its reputation as an ugly to read
         | language with inconsistent syntax: design by committee
        
           | wtetzner wrote:
           | Ugly is subjective, but which part of the syntax is
           | inconsistent?
        
             | arccy wrote:
             | .await something that looks like a field access but
             | actually does something else
        
             | dlisboa wrote:
             | I don't know if this qualifies as inconsistent, but:
             | 
             | `impl T for for<'a> fn(&'a u8) {}`
             | 
             | The `for` word here is used in two different meanings, both
             | different from each other and from the third and more usual
             | `for` loop.
             | 
             | Rust just has very weird syntax decisions. All
             | understandable in isolation but when put altogether it does
             | yield a hard to read language.
        
               | steveklabnik wrote:
               | All three have the same underlying idea: do this for
               | every thing of that. In the first case, it's implement a
               | trait for a type. In the second case, it's "for all
               | choices of the lifetime" and for a for loop, it's do
               | something for each element of a collection.
        
               | dlisboa wrote:
               | I understand how that seems logical in isolation but it's
               | just not how syntax is usually read by people. It's done
               | so as part of a reading context instead of as separate
               | syntatical tokens. The underlying idea is not the same
               | for the reader because the context is vastly different.
        
               | wtetzner wrote:
               | This feels disingenuous. I have a hard time imagining a
               | case where someone would find this confusing.
        
               | steveklabnik wrote:
               | Sure, and I think that's insightful: what you may
               | consider a mess, I may consider orthogonal!
        
               | NobodyNada wrote:
               | Rust tends to _prefer_ reusing keywords (in unambiguously
               | different contexts) rather than introducing new ones,
               | because adding a new keyword is a backwards compatibility
               | break that requires a new language edition and creates
               | migration pain for anyone who had an identifier with the
               | same name as the new keyword.
        
               | steveklabnik wrote:
               | While that's true, all three of these uses pre-date Rust
               | 1.0, so there was total freedom in this case.
        
             | j-krieger wrote:
             | I tended to disagree on this discussion in the past, but I
             | increasingly no longer do. For example, let's have a look
             | at the new `implicit lifetime capturing` syntax:
             | fn f(x: &()) -> impl Sized + use<'_> { x }
             | 
             | It's weird. It's full of sigils. It's not what the Rust
             | team envisioned before a few key members left.
        
           | jeremyjh wrote:
           | Whereas Go has taken the approach of designing everything
           | "later".
        
             | mseepgood wrote:
             | It's good to have languages with different approaches to
             | design and and with different design philosophies.
        
           | zaptheimpaler wrote:
           | This is the kind of criticism made by people who've spent
           | less than a few days working with a language. Just glancing
           | at some code from a distance. There's nothing actually wrong
           | with it besides being foreign from what you are used to.
           | After you gain some familiarity, it doesn't look ugly or
           | beautiful it just looks like syntax.
        
           | codedokode wrote:
           | Golang is also ugly, for example some fields start with a
           | capital letter and some do not.
           | 
           | Also I don't understand how to implement transparent proxies
           | in Go for reactive UI programming.
        
             | arccy wrote:
             | you don't Go is explicit about things.
             | 
             | maybe caps for export is ugly, it's not much different from
             | how python hides with _
        
               | callc wrote:
               | I find both the golang uppercase lowercase naming scheme
               | and python underscores for private (and __ for *extra*
               | private) to be terrible design choices.
               | 
               | They are hidden functionality, a set of rules which must
               | be remembered. "Make sure to do <weird trick> because
               | that mean <X> in <PL>"
               | 
               | Leave identifier names alone. Packing extra info inside
               | is unnecessary mental burden
        
             | joaohaas wrote:
             | If you don't care about field access just always write
             | fields with uppercase. Any APIs you're using only expose
             | uppercased variables as well, so it'll stay consistent.
             | 
             | The public/private stuff is mostly useful for publishing
             | modules with sound APIs.
        
           | olalonde wrote:
           | Isn't "design by popular vote" an extreme version of "design
           | by committee"? Go won't implement error handling syntax
           | because it can't reach community consensus.
        
         | Thaxll wrote:
         | "despite multiple complete perfect proposals on how to do it"
         | 
         | There is no such a thing.
        
         | GoatInGrey wrote:
         | > complete perfect
         | 
         | This is entirely subjective and paints the Go community as
         | being paradoxical, simultaneously obstinate and wanting change.
         | 
         | The disappointing reality is that Go's error handling is the
         | least terrible option in satisfying the language design ethos
         | _and_ developers writing Go. I have a penchant for implementing
         | V 's style of error handling, though I understand why actually
         | implementing it wouldn't be all sunshine and rainbows.
        
           | pie_flavor wrote:
           | No, actually, an operator that's essentially a macro for this
           | entirely boilerplate operation would be less terrible,
           | exactly the same decision Rust made for the exact same
           | reason. So would Cox's proposal, so would others. Doing
           | nothing, as a _permanent solution_ , because you can't figure
           | out which of the better things you should do, is not a
           | virtue. You may be confusing it with the virtue of holding
           | out on simpler solutions while a better solution is
           | implemented, or the virtue of not solving things which aren't
           | problems, but this is a problem and they have intentionally
           | blocked the solution indefinitely.
        
             | tialaramex wrote:
             | Rust's try! macro was+ "essentially a macro for this
             | entirely boilerplate operation" but the Try operator ? is
             | something more interesting because in the process they
             | reified ControlFlow.
             | 
             | Because implementing Try for your own custom types is
             | unstable today if you want to participate you'd most likely
             | provide a ControlFlow yourself. But in doing that you're
             | making plain the distinction between success/ failure and
             | early termination/ continuing.
             | 
             | + Technically still is, Rust's standard library macros are
             | subject to the same policies as the rest of the stdlib and
             | so try! is marked deprecated but won't be removed.
        
             | kiitos wrote:
             | It's just simply not the cause that error handling is an
             | "entirely boilerplate operation", nor that any kind of
             | macro in Go "would be less terrible" than the status quo,
             | nor is it true that decisions that Rust made are even
             | applicable to Go. Believe it or not, the current approach
             | to error handling actually does work and actually is better
             | than most/all proposals thru the lens of Go's original
             | design intent.
        
         | kiitos wrote:
         | Programming languages are designed systems, they need to make
         | sense holistically. They're not simply collections of tick-
         | boxed features that are expected to be added once their tick-
         | box requirements are satisfied.
        
         | bravesoul2 wrote:
         | If Haskell was mainstream and everyone piled in and complained
         | that objects were immutable and it adds so much noise having to
         | deal with that using lenses or state monads or whatever, do we
         | go with democracy or do we say wait.... maybe Haskell was meant
         | to be like this, there are reasons something is a seperate
         | language.
        
       | henry700 wrote:
       | >For a while, the lack of generics surpassed complaints about
       | error handling, but now that Go supports generics, error handling
       | is back on top
       | 
       | >The Go team takes community feedback seriously
       | 
       | It feels like reading satire, but it's real.
        
         | danenania wrote:
         | They clearly are wrestling with these issues, which to me seems
         | like taking the feedback seriously. Taking feedback seriously
         | doesn't imply you have to say yes to every request. That just
         | gives you a design-by-committee junk drawer language. We
         | already have enough of those, so personally I'm glad the Go
         | team sticks to its guns and says no most of the time.
        
           | ummonk wrote:
           | How is Go not a design-by-committee language? They don't have
           | a single lead language developer or benevolent dictator, and
           | as this blog demonstrates, they're very much driven by
           | consensus.
        
             | danenania wrote:
             | True, but they're very picky as committees go. But yeah,
             | maybe not the best use of that expression...
        
       | JamesSwift wrote:
       | I havent followed this argument closely so forgive me if I'm
       | missing relevant discussion, but I dont see why the Rust style
       | isnt just adopted. Its the thing I immediately add now that I
       | have generics in Go.
       | 
       | I only see this blurb in a linked article:
       | 
       | > But Rust has no equivalent of handle: the convenience of the ?
       | operator comes with the likely omission of proper handling.
       | 
       | But I fail to see how having convenience equates to ignoring the
       | error. Thats basically half of my problem with Go's approach,
       | that nothing enforces anything about the result and only
       | minimally enforces checking the error. eg this results in
       | 'declared and not used: err'                 x, err :=
       | strconv.Atoi("123")       fmt.Println("result:", x)
       | 
       | but this runs just fine (and you will have no idea because of the
       | default 0 value for `y`):                 x, err :=
       | strconv.Atoi("123")       if err != nil {         panic(err)
       | }       y, err := strconv.Atoi("1234")
       | fmt.Println("result:", x, y)
       | 
       | this also compiles and runs just fine but again you would have no
       | idea something was wrong                 x, err :=
       | strconv.Atoi("123")       if err != nil {       }
       | fmt.Println("result:", x)
       | 
       | Making the return be `result` _enforces_ that you have to make a
       | decision. Who cares if someone yolos a `!` or conveniently uses
       | `?` but doesnt handle the error case. Are you going to forbid
       | `panic` too?
        
         | umanwizard wrote:
         | Go can't have Result because they don't have sum types, and
         | they can't add them because of their bizarre insistence that
         | every type has to have a designated zero value.
        
           | masklinn wrote:
           | > they can't add them because of their bizarre insistence
           | that every type has to have a designated zero value.
           | 
           | Nothing prevents adding union types with a zero value. Sure
           | it sucks, but so do universal zero values in pretty much
           | every other situation so that's not really a change.
        
             | umanwizard wrote:
             | Making it so all sum types have to be nillable would make
             | them dramatically worse (the basic motivating example for
             | sum types is Option, the whole point of which is to get rid
             | of NULL). I guess this is in agreement with your point.
        
               | masklinn wrote:
               | > the basic motivating example for sum types is Option,
               | the whole point of which is to get rid of NULL
               | 
               | I don't think that's the case in Go: whereas I got the
               | impression the C# team started souring on default() after
               | generics landed (possibly because nullable value types
               | landed alongside and they found out that worked just fine
               | and there was no reason nullable reference types
               | wouldn't) I don't really get that impression from the Go
               | team, even less so from them still mostly being Googlers
               | (proto3 removed both required fields and explicit default
               | values).
        
         | 9rx wrote:
         | _> I dont see why the Rust style isnt just adopted._
         | 
         | Mostly because it is not entirely clear what the Rust-style
         | equivalent in Go might be. What would Rust's "From" look like,
         | for example?
        
           | JamesSwift wrote:
           | Sorry, I wasnt specific in that part. When I say 'rust style'
           | Im really just referring to a union type of `result | error`,
           | with a way to check the state (eg isError and isResult) along
           | with a way to get the state (eg getResult and getError).
           | Optionally '?' and '!' as sugar.
           | 
           | That said, the other responder points out why the sum type
           | approach is not favored (which is news to me, since like I
           | said I havent followed the discussion)
        
             | 9rx wrote:
             | In Go, values are to be always useful, so `result | error`
             | would be logically incorrect. `(result, result | error)`,
             | perhaps - assuming Go had sum types, but that's a bit
             | strange to pass the result twice.
             | 
             | Just more of the pitfalls of it not being clear how Rust-
             | style applies to an entirely different language with an
             | entirely different view of the world.
        
               | JamesSwift wrote:
               | What is useful about the value of `x` in the following
               | code?                 x, err := strconv.Atoi("this is
               | invalid")
               | 
               | On the contrary, `x` is _lying_ to you about being useful
               | and you have absolutely no idea if the string was "0" or
               | "not zero"
        
               | 9rx wrote:
               | _> and you have absolutely no idea if the string was  "0"
               | or "not zero"_
               | 
               | You do - err will tell you. But in practice, how often do
               | you really care?
               | 
               | As Go prescribes "Make the zero value useful" your code
               | will be written in such a way that "0" is what you'll end
               | up using downstream anyway, so _most_ of the time it
               | makes no difference. When it does, err is there to use.
               | 
               | That might not make sense in other languages, but you
               | must remember that they are other languages that see the
               | world differently. Languages are about more than syntax -
               | they encompass a whole way of thinking about programs.
        
               | kbolino wrote:
               | I think it's worth noting that, while the general
               | consensus has converged around (T, error) meaning T XOR
               | error, it does not _necessarily_ mean that. There are
               | some places that violate this assumption, like the
               | io.Reader and io.Writer interfaces. Especially io.Reader,
               | where you can have (n >0, io.EOF), which also isn't even
               | a proper error condition! (This isn't a big problem,
               | though, since you rarely need to directly call Read or
               | Write).
        
               | kiitos wrote:
               | If a function `func foo() (int, error)` returns a non-nil
               | error, then the corresponding `int` is absolutely invalid
               | and should never be evaluated by the caller, unless docs
               | explicitly say otherwise.
        
               | XorNot wrote:
               | And this is why I prefer exceptions.
               | 
               | Errors are common but they _are_ errors: they absolutely
               | represent an exceptional branch of your control flow
               | every time.
               | 
               | It seems reasonable to ask if that int should even be
               | available in the control flow syntactically.
        
             | hobs wrote:
             | To be fair Rust doesn't have sum type it has enums, which I
             | feel like you could do in Go, but I haven't read the
             | arguments.
        
               | 9rx wrote:
               | Technically, Rust has sum types (a.k.a. tagged unions)
               | that use an enum to generate the tag. So, while
               | enumeration is involved, sum types is still a better
               | description of what it is.
        
               | hmry wrote:
               | Enum is what they call it (perhaps to appear more
               | familiar to C++ programmers?), but from a computer
               | science standpoint they are classic sum types.
        
             | kbolino wrote:
             | It's an interesting idea. Right now, you can do something
             | like this:                   res := someFunc() // func()
             | any         switch v := res.(type) {         case error:
             | // handle error         case T:             // handle
             | result         default:             panic("unexpected
             | type!")         }
             | 
             | Then, presumably, a T|error sum type would be a
             | specialization of the any type that would allow you to
             | safely eliminate the default arm of the switch statement
             | (or so I would like to think -- but the zero value issue
             | rears its ugly head here too). Personally, I'd also like to
             | see a refinement of type switches, to allow different
             | variable names for each arm, resulting in something like
             | the following hypothetical syntax:                   switch
             | someFunc().(type) {         case err := error:
             | // handle error         case res := T:             //
             | handle result         }
             | 
             | However, there's no real syntactic benefit _for error
             | handling_ to be found here. I like it (I want discriminated
             | unions too), but it 's really tangential to the problem.
             | I'd honestly prefer it more for other purposes than errors.
        
           | masklinn wrote:
           | > What would Rust's "From" look like, for example?
           | 
           | Idiomatic Go type-erases error types into `error`, when there
           | is even a known type in the first place.
           | 
           | Thus `From` is not a consideration, because the only `From`
           | you need is                   impl<'a, E> From<E> for Box<dyn
           | Error + 'a>         where             E: Error + 'a,
           | 
           | and that means you can just build that in _and nothing else_
           | (and it 's really already built-in by the implicit upcasting
           | of values into interfaces).
        
         | abtinf wrote:
         | > Its the thing I immediately add now that I have generics in
         | Go.
         | 
         | If you're willing to share, I'm very curious to see a code
         | example of what you mean by this.
        
           | JamesSwift wrote:
           | This is from a while back but was the first thing I thought
           | of: https://gist.github.com/J-Swift/96cde097cc324de1f8e899ba3
           | 0a1...
           | 
           | I ripped most of it off of someone else, link in the gist
        
         | masklinn wrote:
         | > But I fail to see how having convenience equates to ignoring
         | the error.
         | 
         | The convenience of writing `?` means nobody will bother
         | wrapping errors anymore. Is what I understand of this extremely
         | dubious argument.
         | 
         | Since you could just design your `?` to encourage wrapping
         | instead.
        
           | NobodyNada wrote:
           | > Since you could just design your `?` to encourage wrapping
           | instead.
           | 
           | Which is exactly what Rust does -- if the error returned by
           | the function does not match the error type of `?` expression,
           | but the error can be converted using the `From` trait, then
           | the conversion is automatically performed. You can write out
           | the conversion implementation manually, or derive it with a
           | crate like thiserror:                   #[derive(Error)]
           | enum MyError {             #[error("Failed to read file")
           | IoError(#[from] std::io::Error)             // ...         }
           | fn foo() -> Result<(), MyError> {             let data =
           | std::fs::read("/some/file")?;             // ...         }
           | 
           | You can also use helper methods on Result (like `map_err`)
           | for inserting explicit conversions between error types:
           | fn foo() -> Result<(), MyError> {             let data =
           | std::fs::read("/some/file").map_err(MyError::IoError)?;
           | // ...         }
        
             | masklinn wrote:
             | 1. That is a global static relationship rather than a local
             | one dynamic one, which is the sense in which Go users use
             | wrapping.
             | 
             | 2. Idiomatic go type erases errors, so you're converting
             | from `error` to `error`, hence type-directed conversions
             | are not even remotely an option.
        
               | alexchamberlain wrote:
               | > That is a global static relationship rather than a
               | local one dynamic one, which is the sense in which Go
               | users use wrapping.
               | 
               | In practice, the error type will be defined quite close
               | to where the conversion is applied, so the static nature
               | of it doesn't feel too big.
        
               | NobodyNada wrote:
               | `map_err` does not need to be type-directed; you can use
               | an arbitrary function or closure. An enum variant can be
               | used as a function mapping from the variant type to the
               | error type, but we can do any arbitrary transformation:
               | .map_err(|e| format!("Failed to read file: {e}")?;
               | 
               | But the "idiomatic Go" way of doing things sounds a lot
               | closer to anyhow in Rust, which provides convenience
               | utilities for dealing with type-erased errors:
               | use anyhow::{Result, Context};              fn foo() ->
               | Result<()> {             let data =
               | std::fs::read("/some/file").context("Failed to read
               | file")?;             // ...         }
        
               | masklinn wrote:
               | Yes, I know that, but the argument (which, again, I
               | called dubious) is that in both cases it's much easier to
               | do just e.g.                 fn foo() -> Result<()> {
               | let data = std::fs::read("/some/file")?;           // ...
               | }
               | 
               | whereas the current morass of Go's error handling means
               | adding wrapping is not much more of a hassle.
               | 
               | But of course even if you accept that assertion you can
               | just design your version of `?` such that wrapping is
               | easier / not wrapping is harder (as it's still something
               | you want) e.g. make it `?"value"` and `?nil` instead of
               | `?`, or something.
        
             | tayo42 wrote:
             | You need to implement from for every type of error then?
             | That seems pretty tedious also.
        
           | sa46 wrote:
           | > The convenience of writing `?` means nobody will bother
           | wrapping errors anymore.
           | 
           | A thread from two days ago bemoans this point:
           | 
           | https://news.ycombinator.com/item?id=44149809
        
         | Pxtl wrote:
         | Wait...                 x, err := strconv.Atoi("123")       if
         | err != nil {         panic(err)       }       y, err :=
         | strconv.Atoi("1234")       fmt.Println("result:", x, y)
         | 
         | > this also compiles and runs just fine but again you would
         | have no idea something was wrong
         | 
         | Okay, I don't use golang... but I thought ":=" was "single
         | statement declare-and-assign".
         | 
         | Is it not redeclaring "err" in your example on line 5, and
         | therefore the new "err" variable (that would shadow the old err
         | variable) should be considered unused and fail with 'declared
         | and not used: err'
         | 
         | Or does := just do vanilla assignment if the variable already
         | exists?
        
           | JamesSwift wrote:
           | As I understand it, go has some special handling for this
           | scenario because its so prevalent which special cases
           | reassignment. The linked article touches on it
           | 
           | > There are exceptions to this rule in areas with high "foot
           | traffic": assignments come to mind. Ironically, the ability
           | to redeclare a variable in short variable declarations (:=)
           | was introduced to address a problem that arose because of
           | error handling: without redeclarations, sequences of error
           | checks require a differently named err variable for each
           | check (or additional separate variable declarations)
        
             | Pxtl wrote:
             | ... I thought Go's whole deal was that you give up the
             | expressiveness and power of overdesigned languages for
             | simple, clean, "only one way to do it" semantics. That
             | "special cases reassignment" where ':=' is sometimes a
             | shadowing declaration and sometimes a reassignment sounds
             | like the opposite of that.
        
               | homebrewer wrote:
               | The language is full of gotchas like that, you're
               | expected to use the tooling to guardrail yourself,
               | because having a proper type system or coherent syntax is
               | "too complicated" (while learning dozens of patterns and
               | weird tricks apparently isn't).
               | 
               | go vet and this massive collection of linters bundled
               | into a single binary are very popular: https://golangci-
               | lint.run
               | 
               | linters will warn you of accidental shadowing, among many
               | other things.
        
           | kbolino wrote:
           | It's trickier than that, unfortunately. There has to be at
           | least one new variable on the left side of := but any other
           | variables that already exist _in the same scope_ will simply
           | be assigned to. However, if you use := in a nested block,
           | then the variable _is_ redeclared and shadows the outer-scope
           | variable.
        
             | Pxtl wrote:
             | Thanks, I hate it.
        
         | mseepgood wrote:
         | - It has poor visibility, it hides control flow branches in a
         | single statement / expression. That's one of the reasons Go got
         | rid of the ternary operator in favor of an if statement where
         | each branch has to be on its own line.
         | 
         | - It isn't easily breakpointable.
         | 
         | - It favors "bubbling up" as-is over enriching or handling.
        
       | _jab wrote:
       | I once had a Go function that, unusually, was _expecting_ an
       | error to be returned from an inner function, and so had to return
       | an error (and do some other processing) if none was returned by
       | the inner function, and return nil if the inner function did
       | return an error.
       | 
       | In a nutshell, this meant I had to do `if err == nil { // return
       | an error }` instead of `if err != nil { ... }`. It sounds simple
       | when I break it down like this, but I accidentally wrote the
       | latter instead of the former, and was apparently so desensitized
       | to the latter construct that it actually took me ages to debug,
       | because my brain simply did not consider that `if err != nil` was
       | not supposed to be there.
       | 
       | I view this as an argument in favor of syntactic sugar for common
       | expressions. Creating more distinction between `if err != nil`
       | (extremely common) and `if err == nil` (quite uncommon) would
       | have been a tangible benefit to me in this case.
        
         | adamrt wrote:
         | Any time I write "if err == nil" I write // inverted just to
         | make it stick out. It would be nice if it was handled by the
         | language but just wanted to share a way to at least make it a
         | bit more visible.                   if err == nil { // inverted
         | return err         }
        
           | macintux wrote:
           | I know diddly/squat about Go, but from similar patterns in
           | aeons past, would "nil == err" work as a way to make it stand
           | out?
        
             | haiku2077 wrote:
             | Just tried this and it appears to be valid in the compiler,
             | formatter and golangci-lint
        
               | _whiteCaps_ wrote:
               | https://en.wikipedia.org/wiki/Yoda_conditions
               | 
               | Works especially well in languages that can make
               | assignments in if statements, e.g:
               | 
               | if foo = 42 { }
        
           | hnlmorg wrote:
           | I do something similar. I leave a comment but with a short
           | comment why it's inverted.
           | 
           | It's usually pretty obvious why: eg                   if err
           | == nil {               // we can exit early because we don't
           | need to keep retrying
           | 
           | But it at least saves me having to double check the logic of
           | the code each time I reread the code for the first time in a
           | while.
        
           | vhcr wrote:
           | Would be nice if code editors colored it differently so it's
           | easier to see.
        
           | prerok wrote:
           | return nil
           | 
           | would be clearer, I think. Seems like it's the same but would
           | color differently in my editor.
        
         | skybrian wrote:
         | Good point. Perhaps it could also be solved in an editor with a
         | collapsed notation like 'if err ... {'
        
         | 9rx wrote:
         | Of course, `if fruit != "Apple" { ... }` would leave you in the
         | exact same situation. Is there a general solution to improving
         | upon this? Seeing it as an error problem exclusively seems
         | rather misguided. After all, there is nothing special or unique
         | about errors. They're just state like any other.
        
           | adamrt wrote:
           | I think its more of a comment that "err != nil" is used in
           | the vast majority of cases, so you start to treat it as noise
           | and skim it.
        
             | 9rx wrote:
             | That reality may make the fundamental flaws of the if
             | statement more noticeable, but at the end of the day the
             | problem is still that the if statement itself is not great.
             | If we're going to put in effort to improve upon it - and it
             | is fair to say that we should - why only for a type named
             | error?
        
         | derefr wrote:
         | Just as a devil's-advocate argument, an IDE + font _could_
         | syntax-highlight + ligature `if err != nil` (only under Golang
         | syntax mode) into a single compact heiroglyph and fade it into
         | the background -- which would in turn make anything that
         | differs from that exact string (like `if err == nil`) now pop
         | out, due to _not_ being rendered that way.
        
         | purpleidea wrote:
         | This is actually an argument _against_ the syntactic changes.
         | Because now if you have the common `if err == nil { return ...
         | }` pattern, then you have _that_  "littering" your code,
         | instead of the syntax.
         | 
         | The current solution is fine, and it seems to be only
         | junior/new to golang people who hate it.
         | 
         | Everyone I know loves the explicit, clear, easy to read
         | "verbose" error handling.
        
       | evmar wrote:
       | Most discussions of language features immediately fall into the
       | politician's syllogism:
       | 
       | https://en.wikipedia.org/wiki/Politician%27s_syllogism
       | 
       | I appreciate the Go language's general sense of conservatism
       | towards change. Even if you're not a fan of it, I think it's
       | admirable that there is a project staking out a unique spot in
       | the churn-vs-stability design space. There are plenty of other
       | projects that churn as fast as they can, which also has its pros
       | and cons, and it's great to be able to see the relative outcomes.
       | 
       | PS: it's kind of hilarious how the blog post is like "there are
       | hundreds of proposals and miles of detailed analysis of these",
       | vs the commenters here who are like "I thought about this for
       | five minutes and I now have an idea that solve everything, let me
       | tell you about it".
        
         | mseepgood wrote:
         | Sometimes doing nothing is the right thing to do. (Quote from
         | Until Dawn)
         | 
         | Go chose not to change the error handling - Nature remained in
         | balance.
        
         | ummonk wrote:
         | I'd happily come up with criticisms of any specific proposal
         | and bikeshed it, but any one of these proposals would be
         | preferable to the status quo.
         | 
         | I'd understand if they decided they needed more time to
         | continue iterating on and analyzing proposals to find the right
         | solution, but simply declaring that they'll just suspend the
         | whole effort because they can't come to a consensus is rather
         | infuriating.
        
           | sa46 wrote:
           | "Simply declaring" is inaccurate description of the Go team's
           | decision. The team built several proposals, reviewed dozens
           | more, and refined the process by gathering user feedback in
           | multiple channels.
           | 
           | The Go team thoroughly explored the design space for seven
           | years and did not find community consensus.
        
             | ummonk wrote:
             | There are two possibilities.
             | 
             | 1) There isn't consensus that improved syntax for error
             | handling is needed in the first place. If that is the case,
             | they should just say so, instead of obfuscating by focusing
             | on the number of proposals and the length of the process.
             | 
             | 2) There is consensus about a need for improved error
             | handling syntax, but after seven years of proposals they
             | haven't been able to find community consensus about the
             | best way to add said syntax. That would mean that improved
             | syntax for error handling is necessary, but the Go team is
             | understandably hesitant to push forward and lock in a
             | potentially inferior solution. If that is the case, then
             | would be reason to continue working on improved syntax for
             | error handling, so as to find the best solution even if it
             | takes a while.
        
       | ddoice wrote:
       | Oh No! Is a much better name for error handling.
        
       | ajkjk wrote:
       | Okay here's my idea, not found on the list in the article, what
       | do you think:
       | 
       | You add a visualization sugar via an IDE plugin that renders
       | if/else statements (either all of them or just error cases) as
       | two separate columns of code --- something like
       | x = foo();          if (x != nil)        | else           <happy
       | case>       | <error case>
       | 
       | And then successive error cases can split further, making more
       | columns, which it is up to the IDE to render in a useful way.
       | Underneath the representation-sugar it's still just a bunch of
       | annoyingly nested {} blocks, but now it's not annoying to look
       | at. And since the sugar is supported by the language developers,
       | everyone is using the same version and can therefore rely on
       | other developers seeing and maintaining the readability of the
       | code in the sugared syntax.
       | 
       | If the error case inside a block returns then its column just
       | ends, but if it re-converges to the main case then you visualize
       | that in the IDE as well. You can also maybe visualize some
       | alternative control flows: for instance, a function that starts
       | in a happy-path column but at all of its errors jumps over into
       | an error column that continues execution (which in code would
       | look like a bunch of `if (x=nil) { goto err; }` cases.
       | 
       | Reason for doing it this way: logical flow within a single
       | function forms a DAG, and trying to represent it linearly is
       | fundamentally doomed. I'm betting that it will eventually be the
       | case that we stop trying to represent it linearly, and we may as
       | well start talking about how to do it now. Sugar is the obvious
       | approach because it minimizes rethinking the underlying language
       | and allows for you to experiment with different approaches.
        
         | Someone wrote:
         | I would simplify that to                 x = foo() ||| <error
         | case>       <happy case>
         | 
         | (With the specific symbol used in lieu of _|||_ to be decided)
         | 
         | That is shorter and keeps the happy path unindented, even if it
         | has additional such constructs, for example                 x =
         | foo() ||| return Error(err, "foo failed")       y = bar() |||
         | return Error(err, "bar failed")
        
         | layer8 wrote:
         | I think that IDE functionality is fine for writing code, but
         | shouldn't be imposed for the UX of reading code, because code
         | reading also happens a lot outside of IDEs, because it
         | constrains the choice of editors, and because it creates
         | fundamentally different "modes" of source code presentation.
         | The visualizations will start to appear in comment threads like
         | this one, and in other publications on the web, but copying
         | them and pasting them into an editor will not work (will be
         | invalid syntax). It creates unnecessary complications across
         | the whole ecosystem. Language syntax should stand on its own,
         | and shouldn't need crutches like that to make it ergonomic to
         | read.
        
           | ajkjk wrote:
           | I have the opposite opinion, I guess! We've been trying to
           | solve everything with language syntax for a long time and
           | it's a bit of a dead end, as the OP shows. Better to start
           | trying new things.
           | 
           | Anyway you can always copy paste it in the normal linear
           | format.
        
       | zahlman wrote:
       | Watching the process of thinking about this from the outside,
       | somehow reminds me of my experience on the inside of the Python
       | community trying to figure out packaging.
        
       | te_chris wrote:
       | The way elixir conventionally uses tuples and pattern matching is
       | really good.
        
       | threemux wrote:
       | If you feel the need (as many have in this thread) to breezily
       | propose something the Go Team could have done instead, I urge you
       | to click the link in the article to the wiki page for this:
       | 
       | https://go.dev/wiki/Go2ErrorHandlingFeedback
       | 
       | or the GitHub issue search:
       | https://github.com/golang/go/issues?q=+is%3Aissue+label%3Aer...
       | 
       | I promise that you are not the first to propose whatever you're
       | proposing, and often it was considered in great depth. I
       | appreciate this honest approach from the Go Team and I continue
       | to enjoy using Go every day at work.
        
         | anentropic wrote:
         | It's probably already answered somewhere, but I am curious why
         | it's such a problem in Go specifically, when nearly every
         | language has something better - various different approaches
         | ... is the problem just not being able to decide / please
         | everyone, or there's something specific about Go the language
         | that means everyone else's solutions don't work somehow?
        
           | ok_dad wrote:
           | Go has specific goals like not hiding control flow. This
           | would go against those goals, at least the ways people have
           | thought to do it so far.
        
             | tialaramex wrote:
             | I don't see how Try (the ? operator) is hidden control
             | flow. It's terse, but it's not hidden.
        
               | ok_dad wrote:
               | I personally agree, but I'm not the go team. The hidden
               | control flow was specifically called out but about the
               | try keyword. I like the ? and similar ways of checking
               | nulls, but personally I don't mind the verbosity in go,
               | even if there are footguns.
        
               | jchw wrote:
               | IMO: because it behaves like structured control flow
               | (i.e. there is a branch) but it doesn't look like
               | structured control flow (i.e. it doesn't look like there
               | is a branch; no curly braces). I don't think there's a
               | single other case in the Go programming language: it
               | doesn't even have the conditional ternary operator, for
               | example.
        
               | LoganDark wrote:
               | `return` doesn't have braces either.
        
               | jchw wrote:
               | Closest thing to a real interblock branch without braces,
               | IMO, is `break` and `continue`, but those are both at
               | least lone statements, not expressions. It "looks like"
               | control flow. Personally, I don't count `return`, I view
               | it as it's own thing from a logical standpoint. Obviously
               | if we were talking about literal CPU doing a jump, well
               | then a lot of things would count, but that's not what I
               | mean in the frame of structured control flow and more in
               | the realm of implementation details.
        
               | 9rx wrote:
               | Even the laziest programmer is going to want to wrap the
               | error with another error, and usually you will want to do
               | more than that.
               | 
               | You can put that in-band, with something like:
               | v := funcWithError()? err := {              return
               | fmt.Errorf("lazy programmer wrapping: %w", err)         }
               | 
               | But in that case what have you really gained?
               | 
               | Or you can do some kind of magic to allow it to happen
               | out of band:                   // found somewhere else in
               | the codebase         func wrapErr(err error) error {
               | if err == nil {                 return nil             }
               | return fmt.Errorf("lazy programmer wrapping: %w", err)
               | }              v := funcWithError()?(wrapErr)
               | 
               | But that's where you start to see things hidden.
        
               | kiitos wrote:
               | Currently if you want to return from a function/method
               | you need to type `return` in the source code. And return
               | is a single expr, it can't be chained or embedded, and in
               | almost all cases it exists on its own line in the file.
               | This is an important invariant for Go, even if you don't
               | understand or buy its motivation. `?` would fundamentally
               | change that core property of control flow. In short,
               | chaining is not considered to be a virtue, and is not
               | something that's desired.
        
               | skywhopper wrote:
               | That only covers one tiny case among several possible
               | error flows. Why add special syntax for that?
        
           | hackingonempty wrote:
           | The language is designed for Google, which hires thousands of
           | newly graduated devs every year. They also have millions of
           | lines of code. In this environment they value easy of
           | onboarding devs and maintaining the codebase over almost
           | everything else. So they are saddled with bad decisions made
           | a long time ago because they are extremely reluctant to
           | introduce any new features and especially breaking changes.
        
           | skywhopper wrote:
           | The thing is, it's not actually a major problem. It's the
           | thing that gets the most complaints for sure, and rubs folks
           | from other languages the wrong way often. But it's an
           | intentional design that is aware of its tradeoffs. As a 10
           | year Go veteran, I strongly prefer Go's approach to most
           | other languages. Implicit control flow is a nightmare that is
           | best avoided, imo.
           | 
           | It's okay for Go to be different than other languages. For
           | folks who can't stand it, there are lots of other options. As
           | it is, Go is massively successful and most active Go
           | programmers don't mind the error handling situation. The
           | complaints are mostly from folks who didn't choose it
           | themselves or don't even actually use it.
           | 
           | The fact that this is the biggest complaint about Go proves
           | to me the language is pretty darn incredible.
        
           | munificent wrote:
           | I think the two big things for Go are:
           | 
           | 1. Minimalism.
           | 
           | Go has always had an ethos of extreme minimalism and have
           | deliberately cultivated an ecosystem and userbase that also
           | places a premium on that. Whereas, say, the Perl ecosystem
           | would be delighted to have the language add one or seven knew
           | ways of solving the same problem, the Go userbase doesn't
           | want that. They want one way to do things and highly value
           | consistency, idiomatic code, and not having to make
           | unnecessary implementation choices when programming.
           | 
           | In every programming language, there is a cost to adding
           | features, but that cost is relatively higher in Go.
           | 
           | 2. Concurrency.
           | 
           | Concurrency, channels, and goroutines are central to the
           | design of the language. While I'm sure you can combine
           | exception handling with CSP-based concurrency, I wouldn't
           | guarantee that the resulting language is easy to use
           | correctly. What happens when an uncaught exception unwinds
           | the entire stack of a goroutine? How does that affect other
           | goroutines that it spawned or that spawned it? What does it
           | do to goroutines that are waiting on channels that expect to
           | hear from it?
           | 
           | There may be a good design there, but it may also be that
           | it's just really really hard to reason about programs that
           | heavily use CSP-style concurrency and exceptions for error
           | handling.
           | 
           | The Go designers cared more about concurrency than error
           | handling, so they chose a simpler error handling model that
           | doesn't interfere with goroutines as much. (I understand that
           | panics complicate this story. I'm not a Go expert. This is
           | just what I've inferred from the outside.)
        
         | hackingonempty wrote:
         | The draft design document that all of the feedback is based on
         | mentions C++, Rust, and Swift. In the extensive feedback
         | document you link above I could not find mention of do-
         | notation/for-comprehensions/monadic-let as used
         | Haskell/Scala/OCaml. I didn't find anything like that in the
         | first few pages of the most commented GitHub issues.
         | 
         | You make it out like the Go Team are programming language
         | design wizards and people here are breezily proposing solutions
         | that they must have considered but lets not forget that the Go
         | team made the same blunder made by Java (static typing with no
         | parametric polymorphism) which lies at the root of this error
         | handling problem, to which they are throwing up their hands and
         | not fixing.
        
           | 9rx wrote:
           | _> lets not forget that the Go team made the same blunder
           | made by Java_
           | 
           | To be fair, they were working on parametric polymorphism
           | since the beginning. There are countless public proposals,
           | and many more that never made it beyond the walls of Google.
           | 
           | Problem was that they struggled to find a design that didn't
           | make the same blunder as Java. I'm sure it would have been
           | easy to add Java-style generics early on, but... yikes. Even
           | the Java team themselves warned the Go team to not make that
           | mistake.
        
             | karmakaze wrote:
             | At least Java supports covariance and contravariance where
             | Go only supports invariant generics.
        
               | PaulHoule wrote:
               | Java has evolved to contain much of "ML the good parts"
               | such as that languages like Kotlin or Scala that offer a
               | chance to be just a bit better in the JVM look less
               | necessary
        
           | platinumrad wrote:
           | Even Rust and F#[1] don't have (generalized) do notation,
           | what makes it remotely relevant to a decidedly non-ML-esque
           | language like Go?
           | 
           | [1] Okay fine, you can fake it with enough SRTPs, but Don
           | Syme will come and burn your house down.
        
             | evntdrvn wrote:
             | hahaha :D
        
             | nine_k wrote:
             | IDK, Python was fine grabbing list comprehensions from
             | Haskell, yield and coroutines from, say, Modula-2, the
             | walrus operator from, say, C, large swaths from Smalltalk,
             | etc. It does not matter if the languages are related; what
             | matters is whether you can make a feature / approach fit
             | the rest of the language.
        
           | munificent wrote:
           | I think Go should have shipped with generics from day one as
           | well.
           | 
           | But you breezily claiming they made the same blunder as Java
           | omits the fact that they _didn 't_ make the same blunder as
           | Rust and Swift and end up with nightmarish compile times
           | because of their type system.
           | 
           | Almost every language feature has difficult trade-offs. They
           | considered iteration time a priority one feature and designed
           | the language as such. It's very easy for someone looking at a
           | language on paper to undervalue that feature but when you sit
           | down and talk to users or watch them work, you realize that a
           | fast feedback loop makes them more productive than almost any
           | brilliant type system feature you can imagine.
        
         | yusina wrote:
         | It fascinates me that really smart and experienced people have
         | written that page and debated approaches for many years, and
         | yet nowhere on that page is the Haskell-solution mentioned,
         | which is the Maybe and Either monads, including their do-
         | notation using the bind operator. Sounds fancy, intimidating
         | even, but is a very elegant and functionally pure way of just
         | propagating an error to where it can be handled, at the same
         | time ensuring it's not forgotten.
         | 
         | This is so entrenched into everybody writing Haskell code, that
         | I really can't comprehend why that was not considered. Surely
         | there must be _somebody_ in the Go community knowing about it
         | and perhaps appreciating it as well? Even if we leave out
         | everybody too intimidated by the supposed academic-ness of
         | Haskell and even avoiding any religios arguments.
         | 
         | I really appreciate the link to this page, and overall its
         | existence, but this really leaves me confused how people caring
         | so much about their language can skip over such well-
         | established solutions.
        
           | jchw wrote:
           | I don't get why people keep thinking it was forgotten; I will
           | just charitably assume that people saying this just don't
           | have much background on the Go programming language. The
           | reason why is because implementing that in any reasonable
           | fashion would require massive changes to the language. For
           | example, you _can 't_ build Either/Maybe in Go (well, of
           | course you _can_ with some hackiness, but it won 't really
           | achieve the same thing) in the first place, and I doubt
           | hacking it in as a magic type that does stuff that can't be
           | done elsewhere is something the Go developers would want to
           | do (any more than they already have to, anyway.)
           | 
           | Am I missing something? Is this really a good idea for a
           | language that can't express monads naturally?
        
             | yusina wrote:
             | > I don't get why people keep thinking it was forgotten
             | 
             | Well, I replied to a post that gave a link to a document
             | that supposedly exhaustively (?) listed all alternatives
             | that were considered. Monads are not on that list. From
             | that, it's easy to come to the conclusion that it was not
             | considered, aka forgotten.
             | 
             | If it was not forgotten, then why is it not on the list?
             | 
             | > Is this really a good idea for a language that can't
             | express monads naturally?
             | 
             | That's a separate question from asking why people think
             | that it wasn't considered. An interesting one though. To an
             | experienced Haskell programmer, it would be worth asking
             | why not take the leap and make it easy to express monads
             | naturally. Solving the error handling case elegantly would
             | just be one side effect that you get out of it. There are
             | many other benefits, but I don't want to make this into a
             | Haskell tutorial.
        
               | jchw wrote:
               | It's not an exhaustive list of every possible way to
               | handle errors, but it is _definitely_ , IMO, roughly an
               | exhaustive list of possible ways Go could reasonably add
               | new error handling tools in the frame of what they
               | already have. The reason why monads and do notation don't
               | show up is because if you try to write such a proposal it
               | _very_ quickly becomes apparent that you couldn 't really
               | add it to the Go programming language without other,
               | _much_ bigger language change proposals (seriously, try
               | it if you don 't believe me.) And for what it's worth,
               | I'm _not_ saying they shouldn 't, it's just that you're
               | taking away the wrong thing; I am absolutely 100% certain
               | this _has_ come up (in fact I think it came up relatively
               | early in one of the GitHub issues), but it hasn 't
               | survived into a proposal for a good reason. If you want
               | this, I believe you can't start with error handling
               | first; sum types would probably be a better place to
               | start.
               | 
               | > That's a separate question from asking why people think
               | that it wasn't considered. An interesting one though. To
               | an experienced Haskell programmer, it would be worth
               | asking why not take the leap and make it easy to express
               | monads naturally. Solving the error handling case
               | elegantly would just be one side effect that you get out
               | of it. There are many other benefits, but I don't want to
               | make this into a Haskell tutorial.
               | 
               | Hmm, but you could say that for any idea that sounds
               | good. Why not add a borrow checker into Go while we're at
               | it, and GADTs, and...
               | 
               | Being blunt, this is just incorrect framing. Concepts
               | like monads and do notation are not inherently "good" or
               | "bad", and neither is a language feature like a borrow
               | checker (which also does _not_ mean you won 't miss it
               | when it's not there in languages like Go, either). Out of
               | context, you can't judge whether it's a good idea or not.
               | In context, we're talking about the Go programming
               | language, which is _not_ a blank slate for programming
               | language design, it 's a pre-existing language with
               | _extremely_ different values from Haskell. It has a pre-
               | existing ecosystem built on this. Go prioritizes
               | simplicity of the language and pragmatism over
               | expressiveness and rich features nearly every time. This
               | is not everyone 's favorite tradeoff, but also,
               | programming language design is not a popularity contest,
               | nor is it an endeavor of mathematical elegance. Designers
               | have goals, often of practical interest, that require
               | trade-offs that by definition not everyone will like. You
               | can't just pretend this constraint doesn't exist or isn't
               | important. (And yes we know, Rob Pike said once in _2012_
               | that Go was for idiots that can 't understand a brilliant
               | language. If anyone is coming here to make sure to reply
               | that under each comment as usual on HN, consider it pre-
               | empted.)
               | 
               | So to answer the question, would it be worth the leap to
               | make it easy to express monads naturally in Go?
               | Obviously, this is a matter of opinion and not fact, but
               | I think this is well beyond the threshold where there is
               | room for ambiguity: _No._ It just does not mesh with it
               | at all, does not match nearly any other decision made
               | anywhere else with regards to syntax and language
               | features, and just generally would feel utterly out of
               | place.
               | 
               | A less general version of this question might be, "OK:
               | how about just sum types and go from there?"--you could
               | _probably_ add sum types and express stuff like Maybe
               | /Either/etc. and add language constructs on top of this,
               | but even that would be a pretty extreme departure and
               | basically constitute a totally new, distinct programming
               | language. Personally, I think there's only one way to
               | look at this: either Go should've had this and the
               | language is basically doomed to always have this flaw,
               | _or_ there is room in the space of programming languages
               | for a language that doesn 't do this without being
               | strictly worse than languages that do (and I'm thinking
               | here in terms of not just elegance or expressiveness but
               | of the second, third, forth, fifth... order effects of
               | such a language design choice, which become increasingly
               | counter-intuitive as you follow the chain.)
               | 
               | And after all, doesn't this _have_ to be the case? If
               | Haskell is the correct language design, then we already
               | have it and would be better off writing Haskell code and
               | working on the GHC. This is not a sarcastic remark: I don
               | 't rule out such dramatic possibilities that some
               | programming languages might just wind up being "right"
               | and win out in the long term. That said, if the winner is
               | going to be Haskell or a derivative of it, I can only
               | imagine it will be a while before that future comes to
               | fruition. A long while...
        
           | joaohaas wrote:
           | It was not forgotten. Maybe/Either and 'do-notation' are
           | literally what Rust does with Option/Result and '?', and that
           | is mentioned a lot.
           | 
           | That said as mentioned in a lot of places, changing errors to
           | be sum types is not the approach they're looking for, since
           | it would create a split between APIs across the ecosystem.
        
         | philosophty wrote:
         | This is a common theme with criticisms of Go.
         | 
         | Relative amateurs assuming that the people who work on Go know
         | _less_ about programming languages than themselves, when in
         | almost all cases they know infinitely more.
         | 
         | The amateur naively assumes that whichever language packs in
         | the most features is the best, especially if it includes their
         | personal favorites.
         | 
         | The way an amateur getting into knife making might look at a
         | Japanese chef's knife and find it lacking. And think they could
         | make an even better one with a 3D printed handle that includes
         | finger grooves, a hidden compartment, a lighter, and a
         | Bluetooth speaker.
        
           | throwawaymaths wrote:
           | To be fair there are lots of people who have used multiple
           | programming languages at expert levels that complain about go
           | - in the same ways - as well! They might not be expert
           | programming language designers, but they have breadth of
           | experience, and even some of them have written their own
           | programming languages too.
           | 
           | Assuming that all complainants are just idiots is purely
           | misinformed and quite frankly a bit of gaslighting.
        
             | philosophty wrote:
             | "To be fair there are lots of pilots who have flown
             | multiple aircraft at an expert level that complain about
             | the Airbus A380 - in the same ways - as well! They might
             | not be expert airplane designers, but they have a breadth
             | of experience, and even some of them have created their own
             | model airplanes too."
             | 
             | Yes, non-experts can have valid criticisms but more often
             | than not they're too ignorant to even understand what
             | trade-offs are involved.
        
               | throwawaymaths wrote:
               | see there you go again assuming. im talking about people
               | who have written programming languages that are used in
               | prod with millions of users, not people with toy
               | languages.
               | 
               | is the entire go community this toxically ignorant?
        
               | philosophty wrote:
               | You said nothing that indicates you were referring to
               | other expert language designers.
               | 
               | This entire thread is full if amateurs making ignorant
               | comments. So what expert criticisms are you referring to?
               | 
               | You accused me of "gaslighting" and being "toxically
               | ignorant" while I have been entirely civil.
        
       | unclad5968 wrote:
       | I don't use Go, but I actually like Go's error handling and I
       | think multiple return values is a better solution than any other
       | language I've used. So much so, I've basically adopted it in my
       | c++ code using std::pair. Errors are a value, and the abstraction
       | over that is unnecessary in my opinion. Rust's result type is
       | just syntactic sugar around the multiple return value approach. I
       | don't care for the syntactic sugar, and doing many things in few
       | lines of code isn't valuable to me, but I suspect this is why
       | people love rust's error handling.
        
         | hmry wrote:
         | > Rust's result type is just syntactic sugar around the
         | multiple return value approach
         | 
         | That's really not true. Multiple return values means you always
         | need to return some return value and some error value, even if
         | they are dummy values (like nil). While a result type / sum
         | type genuinely only contains one branch, not the other.
         | 
         | If you had a language that didn't have nil, it would genuinely
         | be impossible to emulate sum type like behavior on top of
         | multiple return values. It serves as an escape hatch, to create
         | a value of some type when you don't actually have a meaningful
         | value to give.
         | 
         | std::variant / std::expected / std::optional aren't syntactic
         | sugar for std::pair either.
        
       | abtinf wrote:
       | IMHO, the actual problem with go error handling isn't the error
       | handling at all -- it's that multiple return values aren't a
       | first class construct. With proper tuple handling and Go's
       | approach to generics, a lot of these issues would just disappear.
        
         | JamesSwift wrote:
         | This also grinds my gears when converting multiple-return
         | functions to returns-a-channel functions. Generics help with
         | that now.
        
       | kubb wrote:
       | TLDR: we didn't fix it for such a long time that now it's too
       | late to fix it. We won't be fixing it, thanks!
       | 
       | Edit: looking at the results of their H1 2024 survey, they're
       | making a hard turn into AI, and most likely will be developing AI
       | libraries to close the gap with Python.
       | 
       | Don't expect language features.
        
       | codehakase wrote:
       | Can't believe we're going to get GTA 6 before an agreed upon
       | (cleaner) error handling pattern in Go.
        
       | HippoBaro wrote:
       | Just to add my two cents--I've been writing Go professionally for
       | about 10 years, and neither I nor any of my colleagues have had
       | real issues with how Go handles errors.
       | 
       | Newcomers often push back on this aspect of the language (among
       | other things), but in my experience, that usually fades as they
       | get more familiar with Go's philosophy and design choices.
       | 
       | As for the Go team's decision process, I think it's a good thing
       | that the lack of consensus over a long period and many attempts
       | can prompt them to formally define a position.
        
         | dangoodmanUT wrote:
         | This, it's always the new people complaining about error
         | handling.
         | 
         | I have many things to complain about for other languages that
         | I'm sure are top-tier complaints too
        
           | tines wrote:
           | I appreciate the argument that things can often be difficult
           | for noobs but actually fine or better than alternatives once
           | you get used to them.
           | 
           | But on the other hand, people who are "used to the way things
           | are" are often the worst people to evaluate whether changes
           | are beneficial. It seems like the new people are the ones
           | that should be listened to most carefully.
           | 
           | I'm not saying the Go team was wrong in this decision, just
           | that your heuristic isn't necessarily a good one.
        
             | jchw wrote:
             | This logic mostly only makes sense if your goal is
             | primarily to grow the audience and widen the appeal,
             | though. I think at this stage in the Go programming
             | language's lifespan, that is no longer the goal. If
             | anything, Go has probably started to saturate its own sweet
             | spot in some ways and a lot of complaints reveal a
             | difference in values more than they do opportunity for
             | improvement.
             | 
             | To me, it makes sense for the Go team to focus on improving
             | Go for the vast majority of its users over the opinions of
             | people who don't like it that much in the first place.
             | There's millions of lines of code written in Go and those
             | are going to have to be maintained for many years. Of
             | utmost priority in my mind is making Go code more correct
             | (i.e. By adding tools that can make code more correct-by-
             | construction or eliminate classes of errors. I didn't _say_
             | concurrency safety, but... some form of correctness
             | checking for code involving mutexes would be really nice,
             | something like gVisor checklocks but better.)
             | 
             | And on that note, if I could pick something to prioritize
             | to add to Go, it would probably be sum types with pattern
             | matching. I don't think it is extremely likely that we will
             | see those, since it's a massive language change that isn't
             | exactly easy to reconcile with what's already here. (e.g. a
             | `Result` type would naturally emerge from the existence of
             | sum types. Maybe that's an improvement, but _boy_ that is a
             | non-trivial change.)
        
           | jiehong wrote:
           | It's fun, because when a newcomer joins a team, people tend
           | to remind them that their bison is fresh and they might be
           | seeing pain we got accustomed to. That's usually said in a
           | positive manner.
        
             | tialaramex wrote:
             | I'm intrigued as to whether "bison" here is a metaphor (for
             | what?) or a cupertino (an error introduced by auto-correct
             | or predictive text)
        
         | abtinf wrote:
         | I have a similar level of experience with Go, and I would go so
         | far as to say it is in fact one of the best features of the
         | language.
         | 
         | I wouldn't be surprised that when the pro-exception-handling
         | crowd eventually wins, it will lead to hard forks and severe
         | fragmentation of the entire ecosystem.
        
           | jchw wrote:
           | To be honest, I really don't believe that will happen in the
           | future. All of the proposals pretty much just add syntactical
           | sugar, and even those have failed to gain consensus.
        
         | ummonk wrote:
         | Yeah once you've been using it long enough for the Stockholm
         | syndrome to set in, you come to terms with the hostile parts of
         | the language.
        
           | janderland wrote:
           | I suspect a lot of us don't have strong feelings either way
           | and don't find the verbosity "hostile". No need for Stockholm
           | syndrome if you don't feel like a prisoner.
           | 
           | Of course you may have been joking, in which case "haha". xD
        
         | zarzavat wrote:
         | That's just survivorship bias isn't it? The newcomers who find
         | Go's design and/or leadership obnoxious get a job that doesn't
         | involve doing something that they dislike.
        
           | arp242 wrote:
           | That's okay. Not everyone needs to like Go. Pleasing every
           | programmer on the planet is an unreasonable thing to ask for.
           | It's also impossible because some preferences conflict.
        
             | zarzavat wrote:
             | After over a decade of people bringing up the issue in
             | almost every single thread about Go, it's time to give the
             | language what it deserves: no more constructive feedback,
             | snarky dismissals only.
        
       | danenania wrote:
       | I have no problem with Go's error handling. It's not elegant, but
       | it works, and that's very much in keeping with the pragmatic
       | spirit of Go.
       | 
       | I'm actually kind of surprised that it's the top complaint among
       | Go devs. I always thought it was more something that people who
       | don't use Go much complain about.
       | 
       | My personal pet issue is lack of strict null checks--and I'm
       | similarly surprised this doesn't get more discussion. It's a way
       | bigger problem in practice than error handling. It makes programs
       | crash in production all the time, whereas error handling is
       | basically just a question of syntax sugar. _Please_ just give me
       | a way to mark a field in a struct required so the compiler can
       | eliminate nil dereference panics as a class of error. It's opt-in
       | like generics, so I don't see why it would be controversial to
       | anyone?
        
         | CactusRocket wrote:
         | I have 2 problems.
         | 
         | It's too easy to accidentally write `if err == nil` instead of
         | `if err != nil`. I have even seen LLMs erroneously generate the
         | first instead of the latter. And since it's such a tiny
         | difference and the code is riddled with `if err != nil`, it's
         | hard to catch at review time.
         | 
         | Second, you're not forced by the language to do anything with
         | the error at all. There are cases where `err` is used in a
         | function that not handling the `err` return value from a
         | specific function silently compiles. E.g.                   x,
         | err := strconv.Atoi(s1)          if err != nil {
         | panic(err)          }          y, err := strconv.Atoi(s2)
         | fmt.Println(x, y)
         | 
         | I think accidentally allowing such bugs, and making them hard
         | to spot, is a serious design flaw in the language.
        
           | danenania wrote:
           | I guess those are fair criticisms in the abstract, but
           | personally I can't recall a single time either has caused a
           | bug for me in practice. I also can't ever recall seeing an
           | LLM or autocomplete mess it up (just my personal experience--
           | I'm sure it can happen).
        
         | masklinn wrote:
         | > It's opt-in like generics, so I don't see why it would be
         | controversial to anyone?
         | 
         | It "breaks" the language in fundamental ways -- much more
         | fundamental than syntactic sugar for error handling -- by
         | making zero values and thus zero initialisation invalid.
         | 
         | You even get this as a fun interaction with generics:
         | func Zero[T any]() T {             var v T             return v
         | }
        
           | danenania wrote:
           | I don't see how it breaks anything if it's opt-in. By default
           | you get the current behavior with zero value initialization
           | if that's what you want (and in many cases it is). But if
           | you'd rather force an explicit value to be supplied, what's
           | the harm?
        
             | masklinn wrote:
             | > I don't see how it breaks anything if it's opt-in?
             | 
             | Zero values are a fundamental, non-optional, "feature" of
             | Go.
             | 
             | > But if you'd rather force an explicit value to be
             | supplied, what's the harm?
             | 
             | What happens if you use the function above with your type?
             | 
             | Or reflect.Zero?
        
               | danenania wrote:
               | If it would only complain on struct literals that are
               | missing the value (and force a nil check before access if
               | the zero value is nil to prevent panics), that would be
               | enough for me. In that case, your Zero function and
               | reflect.Zero can keep working as-is.
        
               | masklinn wrote:
               | Then I fail to see the point, that is trivial to lint,
               | just enable the exhauststruct checker.
        
               | danenania wrote:
               | I wasn't aware of it--will check it out, thanks.
        
       | ziml77 wrote:
       | They admit it's contrived, but this isn't very convincing
       | func printSum(a, b string) error {             x, err :=
       | strconv.Atoi(a)             if err != nil {
       | return fmt.Errorf("invalid integer: %q", a)             }
       | y, err := strconv.Atoi(b)             if err != nil {
       | return fmt.Errorf("invalid integer: %q", b)             }
       | fmt.Println("result:", x + y)             return nil         }
       | 
       | It's not adding anything that the Atoi function couldn't have
       | reported. That's a perfect case for blindly passing an error up
       | the stack.
        
         | nasretdinov wrote:
         | It does, it says which one of the two integers was incorrect
        
           | Zambyte wrote:
           | Why can't Atoi report that?
        
             | nasretdinov wrote:
             | I think Atoi actually does say that, but it's just a toy
             | example. Most often functions outside the standard library
             | don't contain the arguments in their error values
        
       | tsimionescu wrote:
       | > Going back to actual error handling code, verbosity fades into
       | the background if errors are actually handled. Good error
       | handling often requires additional information added to an error.
       | For instance, a recurring comment in user surveys is about the
       | lack of stack traces associated with an error. This could be
       | addressed with support functions that produce and return an
       | augmented error. In this (admittedly contrived) example, the
       | relative amount of boilerplate is much smaller:
       | [...]        if err != nil {             return
       | fmt.Errorf("invalid integer: %q", a)         }       [...]
       | 
       | It's so funny to me to call "manually supplying stack traces" as
       | "handling an error". By the Go team's definition of handling
       | errors, exceptions* "automatically handle errors for you".
       | 
       | * in any language except C++, of course
        
         | teeray wrote:
         | It's funny to me when people see screenfuls of stack traces and
         | remark how clear and useful it is. Perhaps, but do you really
         | need all that? At what cost to your logs? I'd much rather have
         | a one-liner wrapped error that cuts through all the framework
         | and runtime noise. Yes, I can trace just as effectively
         | (usually better)--the wrapping is very greppable when done
         | well. No, in over a decade of writing Go full time, I have
         | never cared about a runtime function or the other usual verbose
         | garbage in my call stack.
        
           | rwiggins wrote:
           | > how clear and useful it is. Perhaps, but do you really need
           | all that?
           | 
           | Do I _need_ clear and useful things? Maybe not. _Would I like
           | to have them anyway?_ Yes.
           | 
           | Years ago, I configured a Java project's logging framework to
           | automatically exclude all the "uninteresting" frames in stack
           | traces. It was beautiful. Every stack trace showed just the
           | path taken through our application. And we could see the
           | stack of "caused-by" exceptions, and common frames (across
           | exceptions) were automatically cut out, too.
           | 
           | Granted, I'm pretty sure logback's complexity is anathema to
           | Go. But my goodness, it had some nice features...
           | 
           | And then you just throw the stack trace in IntelliJ's
           | "analyze stacktrace" box and you get _clickable links_ to
           | each line in every relevant file... I can dream.
           | 
           | > the wrapping is very greppable when done well
           | 
           | Yeah, that's my other problem with it. _When done well._
           | Every time I write an `if err != nil {}` block, I need to
           | decide whether to return the error as is (`return err`) or
           | decorate it with further context (`return fmt.Errorf("stuff
           | broke: %w", err)`). (Or use `%v` if I don't want to wrap. Yet
           | another little nuance I find myself needing to explain to
           | junior devs over and over. And don't get me started about
           | putting that in the `fmt` package.)
           | 
           | So anyway, I've seen monstrosities of errors where there were
           | 6+ "statement: statement: statement: statement: statement:
           | final error" that felt like a dark comedy. I've also seen
           | very high-level errors where I dearly wished for some
           | intermediate context, but instead just had "failed to do a
           | thing: EOF".
           | 
           | That all being said, stack traces are really expensive. So,
           | you end up with some "fun" optimizations:
           | https://stackoverflow.com/questions/58696093/when-does-
           | jvm-s...
        
       | veggieroll wrote:
       | Error handling is one of my favorite parts of Go. The haters can
       | rip `if err != nil { return fmt.Errorf("error doing thing: %w",
       | err) }` from my cold dead hands.
        
         | ummonk wrote:
         | You don't use gofmt?
        
           | tom_ wrote:
           | The whole point of gofmt is that you type in whatever you
           | like and gofmt sorts it out for you. So if you're typing your
           | code in a HN comment box, you'd surely just enter it roughly
           | like that - though i recommend not even including the
           | syntactically irrelevant spaces shown here. Your space bar
           | does have a MTBF you know, and it's measured in
           | activations... let gofmt take the strain. It can waste your
           | screen space for you!
           | 
           | gofmt is the good bit about working in Go. Pretty much
           | everybody uses it, and so you can use it too. Some other
           | languages have similar tools, but they're not as pervasive,
           | so it's far too easy to end up in a situation where you can't
           | use the tool because it would just make too much of a mess of
           | the inconsistently manually-formatted stuff that's already
           | there.
        
         | thayne wrote:
         | Error handling is the thing I hate the most about go. And none
         | of the serious proposals I've seen would remove your ability to
         | continue using `if err != nil`
        
           | veggieroll wrote:
           | I have yet to see a proposal that retains what I love about
           | the status quo: conscientious error handling.
           | 
           | The language's status quo forces everyone to think about
           | errors more deeply than in other languages and acknowledges
           | that the error case is as critical and worthy of the
           | programmer's attention.
        
             | sedatk wrote:
             | > forces everyone to think about errors more deeply than in
             | other languages
             | 
             | Not really. Rust also forces you think deeply about errors
             | but don't bother you with verbose syntax. I think Swift was
             | also similar.
        
       | hackingonempty wrote:
       | Generators and Goroutines have keywords/syntax in Golang but now
       | they don't want to pile on more to handle errors. They could have
       | had one single bit of syntactic sugar, "do notation", to handle
       | all three and more if they had considered it from the beginning
       | but it seems too late if the language designers are even aware of
       | it. TFA says "If you're wondering if your particular error
       | handling idea was previously considered, read this document!" but
       | that document references languages with ad-hoc solutions (C++,
       | Rust, Swift) and does not reference languages like Haskell,
       | Scala, or OCaml which have the same generic solution known as do-
       | notation, for-comprehensions, and monadic-let respectively.
       | 
       | For example instead of                 func printSum(a, b string)
       | error {           x, err := strconv.Atoi(a)           if err !=
       | nil {               return err           }           y, err :=
       | strconv.Atoi(b)           if err != nil {               return
       | err           }           fmt.Println("result:", x + y)
       | return nil       }
       | 
       | they could have something like this:                 func
       | printSum(a, b string) result[error, unit] {           return for
       | {               x <- strconv.Atoi(a)               y <-
       | strconv.Atoi(b)           } yield fmt.Println("result:", x + y)
       | }
       | 
       | which desugars to:                 func printSum(a, b string)
       | result[error, unit] {           return
       | strconv.Atoi(a).flatMap(func(x string) result[error, unit] {
       | return strconv.Atoi(b).map(func(y string) unit {
       | return fmt.Println("result:", x + y)               }           }
       | }
       | 
       | and unlike ad-hoc solutions this one bit of syntax sugar, where
       | for comprehensions become invocations of map, flatMap, and filter
       | would handle errors, goroutines, channels, generators, lists,
       | loops, and more, because monads are pervasive:
       | https://philipnilsson.github.io/Badness10k/escaping-hell-wit...
        
         | VirusNewbie wrote:
         | I absolutely agree with this, and would be better than all of
         | the other proposals.
         | 
         | Did anyone propose this in one of the many error handling
         | proposals?
        
       | bccdee wrote:
       | Here's a non-syntactic suggestion. Could we just patch gofmt to
       | permit this:                   if err != nil { return nil, err; }
       | 
       | as a well-formatted line of go? Make it a special case somehow.
       | 
       | My only big problem with if err != nil is that it eats up 3 lines
       | minimum. If we could squish it down to 1, I'd be much more
       | content.
        
         | verdverm wrote:
         | What if I wrap the error? Should that be squashed to one line?
         | What is the heuristic?
        
           | bccdee wrote:
           | Sure, why not? Let the programmer decide if they want one
           | line or three lines; the tool can permit both. Gofmt is line-
           | length-agnostic--breaking up long lines is already considered
           | to be the programmer's responsibility.
        
         | mseepgood wrote:
         | How would you set a breakpoint on the error return case?
        
           | bccdee wrote:
           | You wouldn't. Rust's ? operator doesn't permit that either.
           | If you need to put a breakpoint there, put a line break
           | there.
        
             | mseepgood wrote:
             | One reason I consider Rust's approach worse than Go's.
        
         | GoatInGrey wrote:
         | The rub there is that you'll have varying styles in which error
         | handling statements appear in your code. With simplistic
         | instances appearing one way and less simplistic ones appearing
         | similarly or differently depending on where each lands on the
         | complexity spectrum. The idiomatic approach is for all
         | instances to be written in the same way regardless of technical
         | nuances.
         | 
         | All of that aside, I've come to learn that passing errors up
         | the call stack without any wrapping or handling is a code
         | smell. It is less than useless for me to attempt setting the
         | value of cell A1 in an Excel sheet to "Foo" and then receive an
         | out-of-range error because the developer made no attempt to
         | even inform me of the context around the error. Let alone
         | handling the error and attempting to correct state before
         | giving up.
         | 
         | In my Excel example, the cause of the error was a data
         | validation problem a couple columns over (F or so). The error
         | was legitimately useless in troubleshooting.
        
       | nilirl wrote:
       | Error handling is some of the least fun parts of writing code. In
       | all languages.
       | 
       | But in any case, why so much fear of being wrong?
       | 
       | > we have fine-grained control over the language version via
       | go.mod files and file-specific directives
       | 
       | And that may be the real truth of it: Error handling in Go just
       | isn't ... that much of a problem to force action?
        
         | scoodah wrote:
         | > And that may be the real truth of it: Error handling in Go
         | just isn't ... that much of a problem to force action?
         | 
         | I you are right that this is the truth of it. Error handling
         | just isn't that big a problem. 13% reported it on the survey
         | cited. That doesn't seem that significant. And honestly, after
         | writing Go, I barely notice error handling as I'm
         | writing/reading code anyway. If anything I appreciate it a bit
         | more than exceptions.
         | 
         | Always something that can be complained about. But it doesn't
         | mean every complaint is a huge deal.
        
       | arccy wrote:
       | considering the recent post [1] on system correctness in amazon
       | quoting "In 2014, Yuan et al. found that 92% of catastrophic
       | failures in tested distributed systems were triggered by
       | incorrect handling of nonfatal errors."
       | 
       | perhaps it's a good thing that error handling is so explicit, and
       | treated as a regular code path.
       | 
       | [1]: https://news.ycombinator.com/item?id=44135638
        
       | honkycat wrote:
       | awesome I love noisy boilerplate in my code, it isn't annoying at
       | all
        
       | jamamp wrote:
       | I like Go's explicit error handling. In my mind, a function can
       | always succeed (no error), or either succeed or fail. A function
       | that always succeeds is straightforward. If a function fails,
       | then you need to handle its failure, because the outer layer of
       | code can not proceed with failures.
       | 
       | This is where languages diverge. Many languages use exceptions to
       | throw the error until someone explicitly catches it and you have
       | a stack trace of sorts. This might tell you where the error was
       | thrown but doesn't provide a lot of helpful insight all of the
       | time. In Go, I like how I can have some options that I always
       | must choose from when writing code:
       | 
       | 1. Ignore the error and proceed onward (`foo, _ :=
       | doSomething()`)
       | 
       | 2. Handle the error by ending early, but provide no meaningful
       | information (`return nil, err`)
       | 
       | 3. Handle the error by returning early with helpful context
       | (return a general wrapped error)
       | 
       | 4. Handle the error by interpreting the error we received and
       | branching differently on it. Perhaps our database couldn't find a
       | row to alter, so our service layer must return a not found error
       | which gets reflected in our API as a 404. Perhaps our idempotent
       | deletion function encountered a not found error, and interprets
       | that as a success.
       | 
       | In Go 2, or another language, I think the only changes I'd like
       | to see are a `Result<Value, Failure>` type as opposed to nillable
       | tuples (a la Rust/Swift), along with better-typed and enumerated
       | error types as opposed to always using `error` directly to help
       | with error type discoverability and enumeration.
       | 
       | This would fit well for Go 2 (or a new language) because adding
       | Result types on top of Go 1's entrenched idiomatic tuple returns
       | adds multiple ways to do the same thing, which creates confusion
       | and division on Go 1 code.
        
         | barrkel wrote:
         | My experience with errors is that error handling policy should
         | be delegated to the caller. Low level parts of the stack
         | shouldn't be handling errors; they generally don't know what to
         | do.
         | 
         | A policy of handling errors usually ends up turning into a
         | policy of wrapping errors and returning them up the stack
         | instead. A lot of busywork.
        
           | XorNot wrote:
           | At this point I make all my functions return error even if
           | they don't need it. You're usually one change away from
           | discovering they actually do.
        
         | billmcneale wrote:
         | > If a function fails, then you need to handle its failure
         | 
         | And this is exactly where Go fails, because it allows you to
         | completely ignore the error, which will lead to a crash.
         | 
         | I'm a bit baffled that you correctly identified that this is a
         | requirement to produce robust software and yet, you like Go's
         | error handling approach...
        
           | haiku2077 wrote:
           | On every project I ship I require golangci-lint to pass to
           | allow merge, which forces you to explicitly handle or ignore
           | errors. It forbids implicitly ignoring errors.
           | 
           | Note that ignoring errors doesn't necessarily lead ti a
           | crash; there are plenty of functions where an error won't
           | ever happen in practice, either because preconditions are
           | checked by the program before the function call or because
           | the function's implementation has changed and the error
           | return is vestigal.
        
             | etra0 wrote:
             | Yet the problem still has happened on big projects:
             | 
             | https://news.ycombinator.com/item?id=36398874
        
           | pphysch wrote:
           | > which will lead to a crash
           | 
           | No it won't. It _could_ lead to a crash or some other nasty
           | bug, but this is absolutely not a fact you can design around,
           | because it 's not always true.
        
         | ignoramous wrote:
         | At this rate, I suspect Go2 is an ideas lab for what's _never_
         | shipping.
        
       | melodyogonna wrote:
       | Go's error handling fade away after a month of consistent use, I
       | would not risk backwards compatibility over this. In fact, I like
       | the explicit error handling.
        
         | thayne wrote:
         | The proposals are not backwards incompatible. And just because
         | you get used to something doesn't mean it is good.
         | 
         | And FWIW, my hatred of go error handling has not diminished
         | with increased usage.
        
         | sedatk wrote:
         | > I like the explicit error handling
         | 
         | You mean "verbose error handling". All other proposals are also
         | explicit, just not as verbose.
        
       | TZubiri wrote:
       | Love it, focus on building with what we have.
       | 
       | Resist enshittification, the greatest advantage in foundational
       | software is sometimes saying no to new features.
        
       | d3ckard wrote:
       | From the Elixir's developer perspective, this is insane. The
       | issue is solved in Erlang / Elixir by functions commonly
       | returning {:ok, result} or {:error, description_or_struct}
       | tuples. This, together with Elixir's `with` statement allows to
       | group error handling at the bottom, which makes for much nicer
       | readability.
       | 
       | Go could just add an equivalent of `with` clause, which would
       | basically continue with functions as long as error is nil and
       | have an error handling clause at the bottom.
        
         | throwawa14223 wrote:
         | Go's multiple return is in itself insane from my perspective.
         | You cannot 'do' anything with a function that has multiple
         | return types except assign them to a variable.
        
           | masklinn wrote:
           | The saddest part is that Go's designers decided to use MRV
           | but pretty much just as bad tuples: as far as I can tell the
           | only thing Go uses MRV for which tuple wouldn't be better as
           | is post-updating named return values, and you could probably
           | just update those via the container.
           | 
           | Common Lisp actually does cool things (if a bit niche) things
           | with MRVs, they're side-channels through which you can obtain
           | additional information if you need it e.g. every common lisp
           | rounding functions returns rounded value... and the remainder
           | as an extra value.
           | 
           | So if you call                 (let ((v (round 5 2)))
           | (format t "~D" v))
           | 
           | you get 2, but if you                 (multiple-value-bind (q
           | r) (round 5 2)         (format t "~D ~D" q r))
           | 
           | you get 2 and 1.
        
             | bravesoul2 wrote:
             | You can at least in Go do this:
             | 
             | r, err := f()
             | 
             | r := f()
             | 
             | _, err := f()
        
           | 9rx wrote:
           | What else would you want to do with them? Maybe in rare cases
           | you'd want to structure them into an array or something, but
           | the inverse isn't possible either [e.g. func f(a, b, c int)
           | -> f(<destructure array into arguments>)] so it isn't like it
           | is inconsistent.
           | 
           | Perhaps what you are really trying to say is that multiple
           | function arguments is insane full stop. You can pass in an
           | array/tuple to the single input just the same. But pretty
           | much every language has settled on them these days - so it
           | would be utterly bizarre to not support them both in and out.
           | We may not have known any better in the C days, but multiple
           | input arguments with only one output argument is plain crazy
           | in a modern language. You can't even write an identity
           | function.
        
             | throwawaymaths wrote:
             | have a single return value and if you really need MRV,
             | return as a tuple type, which you could destructure.
             | 
             | (this is what zig does)
        
               | 9rx wrote:
               | But then why accept multiple input arguments? Why not
               | limit to a single argument, accepting a tuple where
               | multiple arguments are necessary, for input too?
               | 
               | Where multiple input arguments are present, not having
               | multiple output arguments is just strange.
        
               | throwawaymaths wrote:
               | Exactly. Why not make multiple argument function call
               | syntatic sugar over a ~single argument tuple call?
               | 
               | https://ziglang.org/documentation/0.14.1/#call
        
           | knutzui wrote:
           | That's technically not true.
           | 
           | You can pass multiple return values of a function as
           | parameters to another function if they fit the signature.
           | 
           | for example:                 func process[T any](value T, err
           | error) {         if err != nil {           // handle error
           | }         // handle value       }
           | 
           | this can be used in cases such as control loops, to
           | centralize error handling for multiple separate functions,
           | instead of writing out the error handling separately for each
           | function.                 for {
           | process(fetchFoo(ctx))         process(fetchBar(ctx))       }
        
             | prerok wrote:
             | Well, if fetchBar requires fetchFoo to complete
             | successfully, you still somehow have to handle it.
             | 
             | That said, there are libraries out there that implement
             | Result as generic type and it's fine working with them, as
             | well.
             | 
             | I don't see what the hubbub is all about.
        
       | renewiltord wrote:
       | I am very entertained by this. The Golang community bikeshedded
       | their way into the status quo. Hahaha, I have to say that's a
       | pretty good move by the steering org. Punishes bikeshedding.
        
         | pphysch wrote:
         | It could be seen as a big waste of time, but it sets a good
         | precedent.
        
       | stackedinserter wrote:
       | IDE's could help and just hide standard `if err != nil return
       | fmt.Errorf("sh: %w", err)` blocks for us, or show them in some
       | distinct way, like question mark after the statement.
        
       | Mond_ wrote:
       | I really don't like how this article claims that the primary
       | issue with Go's error handling is that the syntax is too verbose.
       | I don't really care about that.
       | 
       | How about:
       | 
       | - Errors can be dropped silently or accidentally ignored
       | 
       | - function call results cannot be stored or passed around easily
       | due to not being values
       | 
       | - errors.Is being necessary and the whole thing with 'nested'
       | errors being a strange runtime thing that interacts poorly with
       | the type system
       | 
       | - switching on errors being hard
       | 
       | - usage of sentinel values in the standard library
       | 
       | - poor interactions with generics making packages such as
       | errgroup necessary
       | 
       | Did I miss anything?
        
         | GiorgioG wrote:
         | Just remember it took _FOREVER_ for Go to support some form of
         | generics. Go evolution happens at a glacial pace. That 's a
         | feature, not a bug....to many.
        
         | neild wrote:
         | > I really don't like how this article claims that the primary
         | issue with Go's error handling is that the syntax is too
         | verbose.
         | 
         | I don't believe this claim is made anywhere.
         | 
         | We've decided that we are not going to make any further
         | attempts to change the syntax of error handling in the
         | foreseeable future. That frees up attention to consider other
         | issues (with errors or otherwise).
        
           | pas wrote:
           | writing a small book on the topic and somehow missing it is
           | the point, not that technically the text is claiming it, the
           | subtext is doing it.
        
         | VirusNewbie wrote:
         | Agreed, 100%.
         | 
         | We're both Googlers here and this is so disappointing to be let
         | down again by the Go team.
        
       | eximius wrote:
       | Ah, shame. `foo := someFallibleMethod()?` would have been nice
       | for when you don't want to handle/wrap the error.
       | 
       | I'm not super strongly against the constant error checking - I
       | actually think it's good for code health to accept it as inherent
       | complexity - but I do think some minor ergonomics would have been
       | nice.
        
       | pikzel wrote:
       | They still haven't solved shadowing.                 a, err :=
       | foo()       b, err := bar()       if err != nil { // oops, forgot
       | to handle foo()'s err }
       | 
       | This is the illusion of safe error handling.
        
         | AnimalMuppet wrote:
         | I would be _astonished_ if there isn 't an automated tool to
         | check for that at the push of a button. I would be mildly
         | surprised if there isn't a compiler flag to check for it.
        
           | arp242 wrote:
           | Not a compiler check, but staticcheck is widely used:
           | % staticcheck test.go         test.go:7:2: this value of err
           | is never used (SA4006)
        
           | dmitshur wrote:
           | Yeah, as one data point,
           | https://staticcheck.dev/docs/checks/#SA4006 has existed since
           | 2017.
        
         | _benton wrote:
         | It's fairly obvious when writing Go that `err` is being
         | shadowed and needs to be checked after each expression. You
         | should be wrapping them anyways!
        
       | agumonkey wrote:
       | makes you miss the `or die` idiom :)
        
       | anttiharju wrote:
       | I was actually bit scared, so read through the whole post and am
       | happy with the conclusion.
       | 
       | Go is very readable in my experience. I'd like to keep it that
       | way.
        
       | cherryteastain wrote:
       | All they have to do is to expand generics to support generic
       | methods so we can have monadic operations like in C++'s
       | std::expected or Rust's Result, like                   func (r
       | Result[T, E]) AndThen[OtherT any](func(T) Result[OtherT, E])
       | Result[OtherT, E] { ... }
       | 
       | which would enable error handling like                   sum := 0
       | parseAndAdd := func(s string) (func(string)Result[int, error]) {
       | /* returns func which parses and adds to sum */ }         return
       | parseAndAdd(a)().AndThen(parseAndAdd(b))
       | 
       | There's a reason why every other language is converging to that
       | sort of functional setup, as it opens up possibilities such as
       | try-transform generics for ranges.
        
       | tslocum wrote:
       | Seems strange that Rust's "?" gets a mention syntax-wise, but
       | nothing is said about sum types coming to Go. Go's verbose error
       | handling and lack of sum types are my only gripes with the
       | language. It would be nice to see both addressed using Rust's
       | Result type as a model.
        
       | reader_1000 wrote:
       | > For instance, a recurring comment in user surveys is about the
       | lack of stack traces associated with an error. This could be
       | addressed with support functions that produce and return an
       | augmented error.
       | 
       | Languages with stack traces gives this to you for free, in Go,
       | you need to implement it every time. OK, you may be disciplined
       | developer where you always augment the error with the details but
       | not all the team members have the same discipline.
       | 
       | Also the best thing about stack traces is that it gives you the
       | path to the error. If the error is happened in a method that is
       | called from multiple places, with stack traces, you immediately
       | know the call path.
       | 
       | I worked as a sysadmin/SRE style for many years and I had to
       | solve many problems, so I have plenty of experience in
       | troubleshooting and problem solving. When I worked with stack
       | traces, solving easy problems was taking only 1-2 minutes because
       | the problems were obvious, but with Go, even easy problems takes
       | more time because some people just don't augment the errors and
       | use same error messages which makes it a detective work to solve
       | it.
        
       | Pxtl wrote:
       | I've never used Go but having used other languages with
       | exception-based error handling, I get why Go decided to go in a
       | different direction... but reading this over...
       | 
       | Okay, so surely some syntactic sugar could make it more pleasant
       | than the                 if (err != nil) {         return nil,
       | err       }
       | 
       | repeated boilerplate. Like, if that return is a tagged union you
       | could do some kind of pattern matching?
       | 
       | ... oh, Go doesn't have sum-types. Or pattern matching.
       | 
       | Could you at least do some kind of generic error handler so I can
       | call                 y := Handle(MyFuncThatCanReturnError(x))
       | 
       | ?
       | 
       | ... Okay, GoLang does not have tuples. Multiple returns must be
       | handled _separately_.
       | 
       | Okay could I write some kind of higher-order function that
       | handles it in a generic way? Like                 y :=
       | InvokeWithErrorHandler(MyFuncThatCanReturnError, x)
       | 
       | ?
       | 
       | No? That's not an option either?
       | 
       | ... why do you all do this to yourselves?
        
         | tail_exchange wrote:
         | This doesn't actually makes the process simpler.
         | 
         | Error handling in Go is not just writing "if err != nil {
         | return nil, err }" for every line. You are supposed to enrich
         | the error to add more context to it. For example:
         | result, err := addTwoNumbers(a, b)         if err != nil {
         | return fmt.Errorf("addTwoNumbers(%d, %d) = %v", a, b, err)
         | }
         | 
         | This way you can enrich the error message and say what was
         | passed to the function. If you try to abstract this logic with
         | a "Handle" function, you'll just create a mess. You'll save
         | yourself the time of writing an IF statement, but you'll need a
         | bunch of arguments that will just make it harder to use.
         | 
         | Not to mention, those helper functions don't account for cases
         | where you don't just want to bubble up an error. What if you
         | want to do more things, like log, emit metrics, clean up
         | resources, and so on? How do you deal with that with the
         | "Handle()" function?
        
       | Bratmon wrote:
       | Watching go people complaining about how other languages
       | encourage bubbling errors up is always hilarious to me because
       | there is literally nothing you can do with errors in go except
       | bubble them up, log them, or swallow them.
       | 
       | Even the article considers "handling" an error to be synonymous
       | with "Adding more text and bubbling it up"!
        
         | gwd wrote:
         | I mean, yeah, most of the time what you do is add more text and
         | bubble up. But:
         | 
         | 1. The very fact that adding more text isn't really any more
         | verbose than not encourages you to add more text, making errors
         | more informative.
         | 
         | 2. A non-negligible amount of times you do something else:
         | carry on, or do something specific based on what kind of error
         | it was. For instance, ignore an error if it's in a certain
         | class; or create the file if it didn't exist; and so on.
         | 
         | Forcing the error handling doesn't seem to me that different
         | than forcing you to explicitly cast between (say) int and
         | int64. Part of me is annoyed with that too, but then I have
         | PTSD flashbacks from 20 years of C programming and appreciate
         | it.
        
         | tail_exchange wrote:
         | > there is literally nothing you can do with errors in go
         | except bubble them up, log them, or swallow them
         | 
         | You can also add additional context to the error before
         | bubbling it up. But yes, that part of the point. Instead of
         | bubbling them up, the programmer should instead reflect on
         | whether it is better than just log and proceed, or completely
         | swallow them. This is what error handling is about.
        
       | latchkey wrote:
       | Gauntlet, the CoffeeScript of golang, seems to have an
       | interesting approach:
       | 
       | https://gauntletlang.gitbook.io/docs/advanced-features/try-s...
        
       | klabb3 wrote:
       | I love Go, but this is almost farcically hilarious:
       | 
       | > The goal of the proposal process is to reach general consensus
       | about the outcome in a timely manner. If proposal review cannot
       | identify a general consensus in the discussion of the issue on
       | the issue tracker, the usual result is that the proposal is
       | declined.
       | 
       | > None of the error handling proposals reached anything close to
       | a consensus, so they were all declined.
       | 
       | > Should we proceed at all? We think not.
       | 
       | The disconnect here is of course that everyone has opinions and
       | Google being design-by-committee can't make progress on user-
       | visible changes. Leaving the verbose error handling is not the
       | end of the world, but there's something here missing in the
       | process. Don't get me wrong, I love inaction as a default
       | decision, but sometimes a decision is better than nothing. It
       | reminds me of a groups when you can't decide what to have for
       | dinner - the best course of action isn't to not eat at all, it's
       | to accept that everyone won't be happy all the time, and take
       | ownership of that unhappiness, if necessary, during the brief
       | period of time when some people are upset.
       | 
       | I doubt that the best proposals are so horrible for some people
       | that they'd hold a grudge and leave Go. IME these stylistic
       | preferences are as easily abandoned as they are acquired.
       | 
       | To put another way: imagine if gofmt was launched today. It would
       | be absolutely impossible to release through a consensus based
       | process. Just tabs vs spaces would be 100 pages on the issue
       | tracker of people willing to die on that hill. Yet, how many
       | people complain about gofmt now that it's already there? Even the
       | biggest bike shedders enjoy it.
        
         | VirusNewbie wrote:
         | It's true that Google is "design by committee" and consensus
         | driven, but the Go team has been particularly obtuse about
         | things, seemingly not understanding which opinions are valid
         | and which are confused.
        
         | zaptheimpaler wrote:
         | Yeah this bugs me about the decision process too, but Go places
         | more importance on backwards compatibility and stability than
         | most other languages so it does align with their values. I'm
         | not the biggest fan of Go but its nice having a language around
         | that favors simplicity and stability.
        
         | derefr wrote:
         | To take this analysis another level deeper: what has happened
         | here is a classic example of bikeshedding -- and, worse,
         | _fostering_ bikeshedding.
         | 
         |  _Everyone_ feels equipped to have an opinion about  "what
         | should be the syntax for an obvious bit of semantics." There's
         | no expertise required to form such an opinion. And so there are
         | as many opinions on offer as there are Go developers to give
         | them.
         | 
         | Limit input on the subject to just e.g. the people capable of
         | implementing the feature into the Go compiler, though, and a
         | consensus would be reached quickly. Unlike drive-by opinion-
         | havers, the language maintainers -- people who have to actually
         | continue to work with one-another (i.e. negotiate in an
         | indefinite iterated prisoner's dilemma about how other language
         | minutiae will work), are much more willing to give ground "this
         | time" to just move on and get it working.
         | 
         | (Tangent: this "giving ground to get ground later" is commonly
         | called "horse trading", but IMHO that paints it in a too-
         | negative light. Horse trading is often the only reason anything
         | gets done at all!)
        
         | mseepgood wrote:
         | > sometimes a decision is better than nothing
         | 
         | Not in this case. The most popular Go proposal/issue of all
         | times was 'leave "if err != nil" alone':
         | https://github.com/golang/go/issues?q=is%3Aissue%20%20sort%3...
        
           | thayne wrote:
           | If go had started out having different syntax for error
           | handling, would these same people request that it change to
           | what go currently does? Or is this just resistance to change,
           | and wanting to keep what they are used to?
           | 
           | I suspect it is the latter.
        
             | mseepgood wrote:
             | The second most popular issue was adding generics. So it's
             | probably not a resistance to change.
        
         | bloppe wrote:
         | The article seems to admit that a `try` keyword restricted to
         | statements / assignments only would combine the best parts and
         | alleviate the major concerns of both the `try` and `?`
         | proposals. It reads as though the concept has not been
         | seriously discussed due simply to exhaustion.
        
           | sho_hn wrote:
           | Pro and con. It hinders progress, but it can also arrest the
           | "enshittification" of a language that takes place when it
           | slowly adds complexity and features and support for whole
           | paradigms.
           | 
           | Python does offer a lot more utility for the expert these
           | days, but it also went from the maxim of "There is one
           | obvious way to do it" to having 5-6 ways to format strings,
           | adding more things to be familiar with, causing refactoring
           | churn as people chase the latest way to do it, etc.
           | 
           | I'm a C++ developer. I wouldn't want to go back to older
           | versions of the language, but it's also _very hard_ to
           | recruit any newer programmers into using it willingly, and
           | the sheer amount of stuff in it that exists concurrently is a
           | big reason why.
        
         | bicarbonato wrote:
         | > I love inaction as a default decision
         | 
         | The thing is, inaction is not simply "not taking an action";
         | Inaction is taking active action of accepting the current
         | solution.
         | 
         | > I doubt that the best proposals are so horrible for some
         | people that they'd hold a grudge and leave Go.
         | 
         | But people may leave go if they constantly avoid fixing any of
         | problems with the language. The more time passes, the more
         | unhappy people become with the language. It will be a death by
         | a thousand cuts.
         | 
         | I love go. But their constant denial do fix obvious problems is
         | tiring.
        
           | imiric wrote:
           | > But people may leave go if they constantly avoid fixing any
           | of problems with the language.
           | 
           | For many people the current Go error handling just isn't a
           | problem. Some even prefer it over the overengineered
           | solutions in other languages. This brutalist simplicity is a
           | core design feature I personally enjoy the most with Go, and
           | forcing me to use some syntax sugar or new keywords would
           | make it less enjoyable to use. I also don't think that
           | generics were a net benefit, but at least I'm not forced to
           | use them.
           | 
           | Go is a cemented language at this point, warts and all.
           | People who don't like it likely won't come around if the
           | issues they're so vocal about were fixed. It's not like
           | there's a shortage of languages to choose from.
        
         | Mawr wrote:
         | > I love inaction as a default decision, but sometimes a
         | decision is better than nothing. It reminds me of a groups when
         | you can't decide what to have for dinner - the best course of
         | action isn't to not eat at all, it's to accept that everyone
         | won't be happy all the time, and take ownership of that
         | unhappiness, if necessary, during the brief period of time when
         | some people are upset.
         | 
         | Invalid comparison - eating one foodstuff or another affects a
         | few people for a few hours. Significantly changing a popular
         | language affects every single user of it forever.
        
       | mparnisari wrote:
       | I have zero complaints about Go's error handling and I'm happy
       | with the decision!
        
       | shermantanktop wrote:
       | "Not adding extra syntax is in line with one of Go's design
       | rules: do not provide multiple ways of doing the same thing"
       | 
       | And so any change to any existing functionality is a breaking
       | change that invalidates all code which uses that functionality?
       | That design rule smells like hubris on the part of Go's
       | designers. It only works if all future changes are extensions and
       | never amendments.
        
       | karel-3d wrote:
       | Hm, I would agree with this 2 years ago; but now I see how great
       | both iterators and generics were despite my initial personal
       | objections, so I am thinking we should give some less verbose
       | error handling a chance. But alas
        
       | bravesoul2 wrote:
       | Slowly reinventing exceptions seems to go against the spirit of
       | Go. That is what you read is what you get.
       | 
       | Haskell solves this with the do notation, but the price is
       | understanding monads. Go also aims to be easy to understand.
        
         | thayne wrote:
         | The proposals aren't reinventing exceptions. They are just
         | making more ergonomic syntax for doing the same thing go error
         | handling does today.
        
           | bravesoul2 wrote:
           | You can argue that is what an exception is. A more ergonomic
           | way to deal with errors that handles propagation for you.
        
       | JyB wrote:
       | This is such a great move. Faith restored in the language after
       | the generics debacle.
        
         | agluszak wrote:
         | > generics debacle
         | 
         | Why debacle?
        
       | sedatk wrote:
       | I'm so happy that Rust sorted this out way early in its lifetime.
       | Error handling is so nice there.
        
         | tick_tock_tick wrote:
         | Rust is basically someone looking at ML languages and thinking
         | what if we did that but just worse in every way.
        
       | Neku42 wrote:
       | Even if the decision to close the door on this sucks I think they
       | are correct - this is not a syntax problem. Adding sugar will not
       | fix fundamental issues w/ Go's error handling.
       | 
       | They need to add/fix like 5-6 different parts of the language to
       | even begin addressing this in a meaningful way.
        
       | ivanjermakov wrote:
       | No one here mentioned Zig approach there, so I'll share:
       | https://ziglang.org/documentation/master/#try
       | 
       | Zig has try syntax that expands to `expr catch |e| return e`,
       | neatly solving a common use case of returning an error to the
       | caller.
        
         | joaohaas wrote:
         | Zig 'error types' are a sum type, while Go ones are just
         | values.
         | 
         | And even then this is just the same as the '?' operator Rust
         | uses, which is mentioned in the post.
        
       ___________________________________________________________________
       (page generated 2025-06-03 23:00 UTC)